You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
228 lines
6.1 KiB
228 lines
6.1 KiB
// ========================================== |
|
// Copyright 2013 Twitter, Inc |
|
// Licensed under The MIT License |
|
// http://opensource.org/licenses/MIT |
|
// ========================================== |
|
|
|
define( |
|
|
|
[], |
|
|
|
function() { |
|
'use strict'; |
|
|
|
function parseEventArgs(instance, args) { |
|
var element, type, callback; |
|
var end = args.length; |
|
|
|
if (typeof args[end - 1] === 'function') { |
|
end -= 1; |
|
callback = args[end]; |
|
} |
|
|
|
if (typeof args[end - 1] === 'object') { |
|
end -= 1; |
|
} |
|
|
|
if (end == 2) { |
|
element = args[0]; |
|
type = args[1]; |
|
} else { |
|
element = instance.node; |
|
type = args[0]; |
|
} |
|
|
|
return { |
|
element: element, |
|
type: type, |
|
callback: callback |
|
}; |
|
} |
|
|
|
function matchEvent(a, b) { |
|
return ( |
|
(a.element == b.element) && |
|
(a.type == b.type) && |
|
(b.callback == null || (a.callback == b.callback)) |
|
); |
|
} |
|
|
|
function Registry() { |
|
|
|
var registry = this; |
|
|
|
(this.reset = function() { |
|
this.components = []; |
|
this.allInstances = {}; |
|
this.events = []; |
|
}).call(this); |
|
|
|
function ComponentInfo(component) { |
|
this.component = component; |
|
this.attachedTo = []; |
|
this.instances = {}; |
|
|
|
this.addInstance = function(instance) { |
|
var instanceInfo = new InstanceInfo(instance); |
|
this.instances[instance.identity] = instanceInfo; |
|
this.attachedTo.push(instance.node); |
|
|
|
return instanceInfo; |
|
}; |
|
|
|
this.removeInstance = function(instance) { |
|
delete this.instances[instance.identity]; |
|
var indexOfNode = this.attachedTo.indexOf(instance.node); |
|
(indexOfNode > -1) && this.attachedTo.splice(indexOfNode, 1); |
|
|
|
if (!Object.keys(this.instances).length) { |
|
//if I hold no more instances remove me from registry |
|
registry.removeComponentInfo(this); |
|
} |
|
}; |
|
|
|
this.isAttachedTo = function(node) { |
|
return this.attachedTo.indexOf(node) > -1; |
|
}; |
|
} |
|
|
|
function InstanceInfo(instance) { |
|
this.instance = instance; |
|
this.events = []; |
|
|
|
this.addBind = function(event) { |
|
this.events.push(event); |
|
registry.events.push(event); |
|
}; |
|
|
|
this.removeBind = function(event) { |
|
for (var i = 0, e; e = this.events[i]; i++) { |
|
if (matchEvent(e, event)) { |
|
this.events.splice(i, 1); |
|
} |
|
} |
|
}; |
|
} |
|
|
|
this.addInstance = function(instance) { |
|
var component = this.findComponentInfo(instance); |
|
|
|
if (!component) { |
|
component = new ComponentInfo(instance.constructor); |
|
this.components.push(component); |
|
} |
|
|
|
var inst = component.addInstance(instance); |
|
|
|
this.allInstances[instance.identity] = inst; |
|
|
|
return component; |
|
}; |
|
|
|
this.removeInstance = function(instance) { |
|
var index, instInfo = this.findInstanceInfo(instance); |
|
|
|
//remove from component info |
|
var componentInfo = this.findComponentInfo(instance); |
|
componentInfo && componentInfo.removeInstance(instance); |
|
|
|
//remove from registry |
|
delete this.allInstances[instance.identity]; |
|
}; |
|
|
|
this.removeComponentInfo = function(componentInfo) { |
|
var index = this.components.indexOf(componentInfo); |
|
(index > -1) && this.components.splice(index, 1); |
|
}; |
|
|
|
this.findComponentInfo = function(which) { |
|
var component = which.attachTo ? which : which.constructor; |
|
|
|
for (var i = 0, c; c = this.components[i]; i++) { |
|
if (c.component === component) { |
|
return c; |
|
} |
|
} |
|
|
|
return null; |
|
}; |
|
|
|
this.findInstanceInfo = function(instance) { |
|
return this.allInstances[instance.identity] || null; |
|
}; |
|
|
|
this.getBoundEventNames = function(instance) { |
|
return this.findInstanceInfo(instance).events.map(function(ev) { |
|
return ev.type; |
|
}); |
|
}; |
|
|
|
this.findInstanceInfoByNode = function(node) { |
|
var result = []; |
|
Object.keys(this.allInstances).forEach(function(k) { |
|
var thisInstanceInfo = this.allInstances[k]; |
|
if (thisInstanceInfo.instance.node === node) { |
|
result.push(thisInstanceInfo); |
|
} |
|
}, this); |
|
return result; |
|
}; |
|
|
|
this.on = function(componentOn) { |
|
var instance = registry.findInstanceInfo(this), boundCallback; |
|
|
|
// unpacking arguments by hand benchmarked faster |
|
var l = arguments.length, i = 1; |
|
var otherArgs = new Array(l - 1); |
|
for (; i < l; i++) otherArgs[i - 1] = arguments[i]; |
|
|
|
if (instance) { |
|
boundCallback = componentOn.apply(null, otherArgs); |
|
if (boundCallback) { |
|
otherArgs[otherArgs.length-1] = boundCallback; |
|
} |
|
var event = parseEventArgs(this, otherArgs); |
|
instance.addBind(event); |
|
} |
|
}; |
|
|
|
this.off = function(/*el, type, callback*/) { |
|
var event = parseEventArgs(this, arguments), |
|
instance = registry.findInstanceInfo(this); |
|
|
|
if (instance) { |
|
instance.removeBind(event); |
|
} |
|
|
|
//remove from global event registry |
|
for (var i = 0, e; e = registry.events[i]; i++) { |
|
if (matchEvent(e, event)) { |
|
registry.events.splice(i, 1); |
|
} |
|
} |
|
}; |
|
|
|
// debug tools may want to add advice to trigger |
|
registry.trigger = function() {}; |
|
|
|
this.teardown = function() { |
|
registry.removeInstance(this); |
|
}; |
|
|
|
this.withRegistration = function() { |
|
this.after('initialize', function() { |
|
registry.addInstance(this); |
|
}); |
|
|
|
this.around('on', registry.on); |
|
this.after('off', registry.off); |
|
//debug tools may want to add advice to trigger |
|
window.DEBUG && DEBUG.enabled && this.after('trigger', registry.trigger); |
|
this.after('teardown', {obj: registry, fnName: 'teardown'}); |
|
}; |
|
|
|
} |
|
|
|
return new Registry; |
|
} |
|
);
|
|
|