/** * 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, clearPropertiesOnDestroy: false, clearPrototypeOnDestroy: false, disabled: 0, constructor: function(config) { Ext.apply(this, config); }, destroy: function() { this.unbind(); this.callParent(); }, 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.destroyed) { me.onContainerRemove(target, target); target.un('beforedestroy', me.disable, me); } me.items = Ext.destroy(me.items); }, disable: function(){ ++this.disabled; }, enable: function(){ if (this.disabled > 0) { --this.disabled; } }, handleAdd: function(ct, comp) { if (!this.disabled) { if (Ext.ComponentQuery.is(comp, 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); } } // This is a temporary hack until we refactor Forms // and kill off Ext.container.Monitor comp.clearPropertiesOnDestroy = comp.clearPrototypeOnDestroy = false; }, 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, i, comp; if (ct.isContainer) { ct.on({ scope: me, add: me.handleAdd, dockedadd: me.handleAdd, remove: me.handleRemove, dockedremove: me.handleRemove }); } // 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); } // This is a temporary hack until we refactor Forms // and kill off Ext.container.Monitor ct.clearPropertiesOnDestroy = ct.clearPrototypeOnDestroy = false; }, 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 (Ext.ComponentQuery.is(comp, 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.isContainer) { me.removeCtListeners(comp); if (!comp.destroying) { 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]); } } } // comp destroying, or we need to invalidate the collection me.invalidateItems(true); }, removeCtListeners: function(ct) { var me = this; ct.un({ scope: me, add: me.handleAdd, dockedadd: me.handleAdd, remove: me.handleRemove, dockedremove: me.handleRemove }); }, 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 = Ext.destroy(me.items); }});