/**
* @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);
}
}
}
}
});