/** * @private */ Ext.define('Ext.event.publisher.ComponentDelegation', { extend: 'Ext.event.publisher.Publisher', requires: [ 'Ext.Component', 'Ext.ComponentQuery' ], targetType: 'component', optimizedSelectorRegex: /^#([\w\-]+)((?:[\s]*)>(?:[\s]*)|(?:\s*))([\w\-]+)$/i, handledEvents: ['*'], getSubscribers: function(eventName, createIfNotExist) { var subscribers = this.subscribers, eventSubscribers = subscribers[eventName]; if (!eventSubscribers && createIfNotExist) { eventSubscribers = subscribers[eventName] = { type: { $length: 0 }, selector: [], $length: 0 } } return eventSubscribers; }, subscribe: function(target, eventName) { // Ignore id-only selectors since they are already handled if (this.idSelectorRegex.test(target)) { return false; } var optimizedSelector = target.match(this.optimizedSelectorRegex), subscribers = this.getSubscribers(eventName, true), typeSubscribers = subscribers.type, selectorSubscribers = subscribers.selector, id, isDescendant, type, map, subMap; if (optimizedSelector !== null) { id = optimizedSelector[1]; isDescendant = optimizedSelector[2].indexOf('>') === -1; type = optimizedSelector[3]; map = typeSubscribers[type]; if (!map) { typeSubscribers[type] = map = { descendents: { $length: 0 }, children: { $length: 0 }, $length: 0 } } subMap = isDescendant ? map.descendents : map.children; if (subMap.hasOwnProperty(id)) { subMap[id]++; return true; } subMap[id] = 1; subMap.$length++; map.$length++; typeSubscribers.$length++; } else { if (selectorSubscribers.hasOwnProperty(target)) { selectorSubscribers[target]++; return true; } selectorSubscribers[target] = 1; selectorSubscribers.push(target); } subscribers.$length++; return true; }, unsubscribe: function(target, eventName, all) { var subscribers = this.getSubscribers(eventName); if (!subscribers) { return false; } var match = target.match(this.optimizedSelectorRegex), typeSubscribers = subscribers.type, selectorSubscribers = subscribers.selector, id, isDescendant, type, map, subMap; all = Boolean(all); if (match !== null) { id = match[1]; isDescendant = match[2].indexOf('>') === -1; type = match[3]; map = typeSubscribers[type]; if (!map) { return true; } subMap = isDescendant ? map.descendents : map.children; if (!subMap.hasOwnProperty(id) || (!all && --subMap[id] > 0)) { return true; } delete subMap[id]; subMap.$length--; map.$length--; typeSubscribers.$length--; } else { if (!selectorSubscribers.hasOwnProperty(target) || (!all && --selectorSubscribers[target] > 0)) { return true; } delete selectorSubscribers[target]; Ext.Array.remove(selectorSubscribers, target); } if (--subscribers.$length === 0) { delete this.subscribers[eventName]; } return true; }, notify: function(target, eventName) { var subscribers = this.getSubscribers(eventName), id, component; if (!subscribers || subscribers.$length === 0) { return false; } id = target.substr(1); component = Ext.ComponentManager.get(id); if (component) { this.dispatcher.doAddListener(this.targetType, target, eventName, 'publish', this, { args: [eventName, component] }, 'before'); } }, matchesSelector: function(component, selector) { return Ext.ComponentQuery.is(component, selector); }, dispatch: function(target, eventName, args, connectedController) { this.dispatcher.doDispatchEvent(this.targetType, target, eventName, args, null, connectedController); }, publish: function(eventName, component) { var subscribers = this.getSubscribers(eventName); if (!subscribers) { return; } var eventController = arguments[arguments.length - 1], typeSubscribers = subscribers.type, selectorSubscribers = subscribers.selector, args = Array.prototype.slice.call(arguments, 2, -2), types = component.xtypesChain, descendentsSubscribers, childrenSubscribers, parentId, ancestorIds, ancestorId, parentComponent, selector, i, ln, type, j, subLn; for (i = 0, ln = types.length; i < ln; i++) { type = types[i]; subscribers = typeSubscribers[type]; if (subscribers && subscribers.$length > 0) { descendentsSubscribers = subscribers.descendents; if (descendentsSubscribers.$length > 0) { if (!ancestorIds) { ancestorIds = component.getAncestorIds(); } for (j = 0, subLn = ancestorIds.length; j < subLn; j++) { ancestorId = ancestorIds[j]; if (descendentsSubscribers.hasOwnProperty(ancestorId)) { this.dispatch('#' + ancestorId + ' ' + type, eventName, args, eventController); } } } childrenSubscribers = subscribers.children; if (childrenSubscribers.$length > 0) { if (!parentId) { if (ancestorIds) { parentId = ancestorIds[0]; } else { parentComponent = component.getParent(); if (parentComponent) { parentId = parentComponent.getId(); } } } if (parentId) { if (childrenSubscribers.hasOwnProperty(parentId)) { this.dispatch('#' + parentId + ' > ' + type, eventName, args, eventController); } } } } } ln = selectorSubscribers.length; if (ln > 0) { for (i = 0; i < ln; i++) { selector = selectorSubscribers[i]; if (this.matchesSelector(component, selector)) { this.dispatch(selector, eventName, args, eventController); } } } } });