/** * Provides a registry of all Components (instances of {@link Ext.Component} or any subclass * thereof) on a page so that they can be easily accessed by {@link Ext.Component component} * {@link Ext.Component#id id} (see {@link #get}, or the convenience method * {@link Ext#getCmp Ext.getCmp}). * * This object also provides a registry of available Component *classes* indexed by a * mnemonic code known as the Component's {@link Ext.Component#xtype xtype}. The `xtype` * provides a way to avoid instantiating child Components when creating a full, nested * config object for a complete Ext page. * * A child Component may be specified simply as a *config object* as long as the correct * `{@link Ext.Component#xtype xtype}` is specified so that if and when the Component * needs rendering, the correct type can be looked up for lazy instantiation. * * @singleton */Ext.define('Ext.ComponentManager', { alternateClassName: 'Ext.ComponentMgr', singleton: true, count: 0, typeName: 'xtype', /** * @private */ constructor: function(config) { var me = this; Ext.apply(me, config || {}); me.all = {}; me.references = {}; me.onAvailableCallbacks = {}; }, /** * Creates a new Component from the specified config object using the config object's * `xtype` to determine the class to instantiate. * * @param {Object} config A configuration object for the Component you wish to create. * @param {String} [defaultType] The `xtype` to use if the config object does not * contain a `xtype`. (Optional if the config contains a `xtype`). * @return {Ext.Component} The newly instantiated Component. */ create: function (config, defaultType) { if (typeof config === 'string') { return Ext.widget(config); } if (config.isComponent) { return config; } if ('xclass' in config) { return Ext.create(config.xclass, config); } return Ext.widget(config.xtype || defaultType, config); }, /** * Returns an item by id. * @param {String} id The id of the item * @return {Object} The item, undefined if not found. */ get: function(id) { return this.all[id]; }, register: function(component) { var me = this, all = me.all, key = component.getId(), onAvailableCallbacks = me.onAvailableCallbacks; //<debug> if (key === undefined) { Ext.raise('Component id is undefined. Please ensure the component has an id.'); } if (key in all) { Ext.raise('Registering duplicate component id "' + key + '"'); } //</debug> all[key] = component; if (component.getReference && component.getReference()) { me.references[key] = component; } ++me.count; if (!me.hasFocusListener) { Ext.on('focus', me.onGlobalFocus, me); me.hasFocusListener = true; } onAvailableCallbacks = onAvailableCallbacks && onAvailableCallbacks[key]; if (onAvailableCallbacks && onAvailableCallbacks.length) { me.notifyAvailable(component); } }, unregister: function(component) { var id = component.getId(); if (component.getReference && component.getReference()) { this.references[id] = null; delete this.references[id]; } this.all[id] = null; delete this.all[id]; this.count--; }, markReferencesDirty: function() { this.referencesDirty = true; }, fixReferences: function() { var me = this, references = me.references, key; if (me.referencesDirty) { for (key in references) { if (references.hasOwnProperty(key)) { references[key].fixReference(); } } me.referencesDirty = false; } }, /** * Registers a function that will be called (a single time) when an item with the specified id is added to the manager. * This will happen on instantiation. * @param {String} id The item id * @param {Function} fn The callback function. Called with a single parameter, the item. * @param {Object} scope The scope ('this' reference) in which the callback is executed. * Defaults to the item. */ onAvailable : function(id, fn, scope){ var me = this, callbacks = me.onAvailableCallbacks, all = me.all, item; if (id in all) { //if already an instance, callback immediately item = all[id]; fn.call(scope || item, item); } else if (id) { // otherwise, queue for dispatch if (!Ext.isArray(callbacks[id])) { callbacks[id] = [ ]; } callbacks[id].push( function(item) { fn.call(scope || item, item);} ); } }, /** * @private */ notifyAvailable : function(item) { var callbacks = this.onAvailableCallbacks[item && item.getId()] || []; while (callbacks.length) { (callbacks.shift())(item); } }, /** * Executes the specified function once for each item in the collection. * @param {Function} fn The function to execute. * @param {String} fn.key The key of the item * @param {Number} fn.value The value of the item * @param {Number} fn.length The total number of items in the collection ** Removed * in 5.0 ** * @param {Boolean} fn.return False to cease iteration. * @param {Object} scope The scope to execute in. Defaults to `this`. */ each: function(fn, scope){ return Ext.Object.each(this.all, fn, scope); }, /** * Gets the number of items in the collection. * @return {Number} The number of items in the collection. */ getCount: function() { return this.count; }, /** * Returns an array of all components * @return {Array} */ getAll: function() { return Ext.Object.getValues(this.all); }, /** * Return the currently active (focused) Component * * @return {Ext.Component/null} Active Component, or null * @private */ getActiveComponent: function() { return Ext.Component.fromElement(Ext.dom.Element.getActiveElement()); }, // Deliver focus events to Component onGlobalFocus: function(e) { var me = this, toElement = e.toElement, fromElement = e.fromElement, toComponent = Ext.Component.fromElement(toElement), fromComponent = Ext.Component.fromElement(fromElement), commonAncestor, targetComponent; // Focus moves *within* a component should not cause component focus leave/enter if (toComponent === fromComponent) { return; } commonAncestor = me.getCommonAncestor(fromComponent, toComponent); if (fromComponent && !(fromComponent.destroyed || fromComponent.destroying)) { if (fromComponent.handleBlurEvent) { fromComponent.handleBlurEvent(e); } // Call onFocusLeave on the component axis from which focus is exiting for (targetComponent = fromComponent; targetComponent && targetComponent !== commonAncestor; targetComponent = targetComponent.getRefOwner()) { if (!(targetComponent.destroyed || targetComponent.destroying)) { targetComponent.onFocusLeave({ event: e.event, type: 'focusleave', target: fromElement, relatedTarget: toElement, fromComponent: fromComponent, toComponent: toComponent }); } } } if (toComponent && !toComponent.destroyed) { if (toComponent.handleFocusEvent) { toComponent.handleFocusEvent(e); } // Call onFocusEnter on the component axis to which focus is entering for (targetComponent = toComponent; targetComponent && targetComponent !== commonAncestor; targetComponent = targetComponent.getRefOwner()) { targetComponent.onFocusEnter({ event: e.event, type: 'focusenter', relatedTarget: fromElement, target: toElement, fromComponent: fromComponent, toComponent: toComponent }); } } }, getCommonAncestor: function(compA, compB) { if (compA === compB) { return compA; } while (compA && !(compA.isAncestor(compB) || compA === compB)) { compA = compA.getRefOwner(); } return compA; }, privates: { clearAll: function() { this.all = {}; this.references = {}; this.onAvailableCallbacks = {}; }, /** * Find the Widget or Component to which the given Element belongs. * * @param {Ext.dom.Element/HTMLElement} el The element from which to start to find * an owning Component. * @param {Ext.dom.Element/HTMLElement} [limit] The element at which to stop upward * searching for an owning Component, or the number of Components to traverse before * giving up. Defaults to the document's HTML element. * @param {String} [selector] An optional {@link Ext.ComponentQuery} selector to * filter the target. * @return {Ext.Widget/Ext.Component} The widget, component or `null`. * * @private * @since 6.0.1 */ fromElement: function (node, limit, selector) { var target = Ext.getDom(node), cache = this.all, depth = 0, topmost, cmpId, cmp; if (typeof limit !== 'number') { topmost = Ext.getDom(limit); limit = Number.MAX_VALUE; } while (target && target.nodeType === 1 && depth < limit && target !== topmost) { cmpId = target.getAttribute('data-componentid') || target.id; if (cmpId) { cmp = cache[cmpId]; if (cmp && (!selector || Ext.ComponentQuery.is(cmp, selector))) { return cmp; } // Increment depth on every *Component* found, not Element depth++; } target = target.parentNode; } return null; } }, deprecated: { 5: { methods: { /** * Checks if an item is registered. * @param {String} component The mnemonic string by which the class may be looked up. * @return {Boolean} Whether the type is registered. * @deprecated 5.0 */ isRegistered: null, /** * Registers a new item constructor, keyed by a type key. * @param {String} type The mnemonic string by which the class may be looked up. * @param {Function} cls The new instance class. * @deprecated 5.0 */ registerType: null } } }},function () { /** * This is shorthand reference to {@link Ext.ComponentManager#get}. * Looks up an existing {@link Ext.Component Component} by {@link Ext.Component#id id} * * @param {String} id The component {@link Ext.Component#id id} * @return {Ext.Component} The Component, `undefined` if not found, or `null` if a * Class was found. * @member Ext */ Ext.getCmp = function(id) { return Ext.ComponentManager.get(id); };});