/** * This is a utility class for being able to track all items of a particular type * inside any level at a container. This can be used in favour of bubbling add/remove events * which can add a large perf cost when implemented globally * @private */Ext.define('Ext.container.Monitor', { target: null, selector: '', scope: null, addHandler: null, removeHandler: null, invalidateHandler: null, disabled: 0, constructor: function(config){ Ext.apply(this, config); }, bind: function(target){ var me = this; me.target = target; target.on('beforedestroy', me.disable, me); me.onContainerAdd(target); }, unbind: function() { var me = this, target = me.target; if (target) { target.un('beforedestroy', me.disable, me); } me.items = null; }, disable: function(){ ++this.disabled; }, enable: function(){ if (this.disabled > 0) { --this.disabled; } }, handleAdd: function(ct, comp) { if (!this.disabled) { if (comp.is(this.selector)) { this.onItemAdd(comp.ownerCt, comp); } if (comp.isQueryable) { this.onContainerAdd(comp); } } }, onItemAdd: function(ct, comp){ var me = this, items = me.items, handler = me.addHandler; if (!me.disabled) { if (handler) { handler.call(me.scope || comp, comp); } if (items) { items.add(comp); } } }, onItemRemove: function(ct, comp){ var me = this, items = me.items, handler = me.removeHandler; if (!me.disabled) { if (handler) { handler.call(me.scope || comp, comp); } if (items) { items.remove(comp); } } }, onContainerAdd: function(ct, preventChildren) { var me = this, items, len, handleAdd = me.handleAdd, handleRemove = me.handleRemove, i, comp; if (ct.isContainer) { ct.on('add', handleAdd, me); ct.on('dockedadd', handleAdd, me); ct.on('remove', handleRemove, me); ct.on('dockedremove', handleRemove, me); } // Means we've been called by a parent container so the selector // matchers have already been processed if (preventChildren !== true) { items = ct.query(me.selector); for (i = 0, len = items.length; i < len; ++i) { comp = items[i]; me.onItemAdd(comp.ownerCt, comp); } } items = ct.query('>container'); for (i = 0, len = items.length; i < len; ++i) { me.onContainerAdd(items[i], true); } }, handleRemove: function(ct, comp) { var me = this; // During a destroy we don't want to maintain any of this information, // so typically we'll be disabled here if (!me.disabled) { if (comp.is(me.selector)) { me.onItemRemove(ct, comp); } if (comp.isQueryable) { me.onContainerRemove(ct, comp); } } }, onContainerRemove: function(ct, comp){ var me = this, items, i, len, item; // If it's not a container, it means it's a queryable that isn't a container. // For example a button with a menu if (!comp.destroyed && !comp.destroying && comp.isContainer) { me.removeCtListeners(comp); items = comp.query(me.selector); for (i = 0, len = items.length; i < len; ++i) { item = items[i]; me.onItemRemove(item.ownerCt, item); } items = comp.query('container'); for (i = 0, len = items.length; i < len; ++i) { me.removeCtListeners(items[i]); } } else { // comp destroying, or we need to invalidate the collection me.invalidateItems(true); } }, removeCtListeners: function(comp){ var me = this; comp.un('add', me.handleAdd, me); comp.un('dockedadd', me.handleAdd, me); comp.un('remove', me.handleRemove, me); comp.un('dockedremove', me.handleRemove, me); }, getItems: function(){ var me = this, items = me.items; if (!items) { items = me.items = new Ext.util.MixedCollection(); items.addAll(me.target.query(me.selector)); } return items; }, invalidateItems: function(triggerHandler) { var me = this, handler = me.invalidateHandler; if (triggerHandler && handler) { handler.call(me.scope || me, me); } me.items = null; }});