1 // ==========================================
2 // Copyright 2013 Twitter, Inc
3 // Licensed under The MIT License
4 // http://opensource.org/licenses/MIT
5 // ==========================================
14 function parseEventArgs(instance, args) {
15 var element, type, callback;
16 var end = args.length;
18 if (typeof args[end - 1] === 'function') {
23 if (typeof args[end - 1] === 'object') {
31 element = instance.node;
42 function matchEvent(a, b) {
44 (a.element == b.element) &&
46 (b.callback == null || (a.callback == b.callback))
54 (this.reset = function() {
56 this.allInstances = {};
60 function ComponentInfo(component) {
61 this.component = component;
65 this.addInstance = function(instance) {
66 var instanceInfo = new InstanceInfo(instance);
67 this.instances[instance.identity] = instanceInfo;
68 this.attachedTo.push(instance.node);
73 this.removeInstance = function(instance) {
74 delete this.instances[instance.identity];
75 var indexOfNode = this.attachedTo.indexOf(instance.node);
76 (indexOfNode > -1) && this.attachedTo.splice(indexOfNode, 1);
78 if (!Object.keys(this.instances).length) {
79 //if I hold no more instances remove me from registry
80 registry.removeComponentInfo(this);
84 this.isAttachedTo = function(node) {
85 return this.attachedTo.indexOf(node) > -1;
89 function InstanceInfo(instance) {
90 this.instance = instance;
93 this.addBind = function(event) {
94 this.events.push(event);
95 registry.events.push(event);
98 this.removeBind = function(event) {
99 for (var i = 0, e; e = this.events[i]; i++) {
100 if (matchEvent(e, event)) {
101 this.events.splice(i, 1);
107 this.addInstance = function(instance) {
108 var component = this.findComponentInfo(instance);
111 component = new ComponentInfo(instance.constructor);
112 this.components.push(component);
115 var inst = component.addInstance(instance);
117 this.allInstances[instance.identity] = inst;
122 this.removeInstance = function(instance) {
123 var index, instInfo = this.findInstanceInfo(instance);
125 //remove from component info
126 var componentInfo = this.findComponentInfo(instance);
127 componentInfo && componentInfo.removeInstance(instance);
129 //remove from registry
130 delete this.allInstances[instance.identity];
133 this.removeComponentInfo = function(componentInfo) {
134 var index = this.components.indexOf(componentInfo);
135 (index > -1) && this.components.splice(index, 1);
138 this.findComponentInfo = function(which) {
139 var component = which.attachTo ? which : which.constructor;
141 for (var i = 0, c; c = this.components[i]; i++) {
142 if (c.component === component) {
150 this.findInstanceInfo = function(instance) {
151 return this.allInstances[instance.identity] || null;
154 this.findInstanceInfoByNode = function(node) {
156 Object.keys(this.allInstances).forEach(function(k) {
157 var thisInstanceInfo = this.allInstances[k];
158 if (thisInstanceInfo.instance.node === node) {
159 result.push(thisInstanceInfo);
165 this.on = function(componentOn) {
166 var instance = registry.findInstanceInfo(this), boundCallback;
168 // unpacking arguments by hand benchmarked faster
169 var l = arguments.length, i = 1;
170 var otherArgs = new Array(l - 1);
171 for (; i < l; i++) otherArgs[i - 1] = arguments[i];
174 boundCallback = componentOn.apply(null, otherArgs);
176 otherArgs[otherArgs.length-1] = boundCallback;
178 var event = parseEventArgs(this, otherArgs);
179 instance.addBind(event);
183 this.off = function(/*el, type, callback*/) {
184 var event = parseEventArgs(this, arguments),
185 instance = registry.findInstanceInfo(this);
188 instance.removeBind(event);
191 //remove from global event registry
192 for (var i = 0, e; e = registry.events[i]; i++) {
193 if (matchEvent(e, event)) {
194 registry.events.splice(i, 1);
199 // debug tools may want to add advice to trigger
200 registry.trigger = function() {};
202 this.teardown = function() {
203 registry.removeInstance(this);
206 this.withRegistration = function() {
207 this.after('initialize', function() {
208 registry.addInstance(this);
211 this.around('on', registry.on);
212 this.after('off', registry.off);
213 //debug tools may want to add advice to trigger
214 window.DEBUG && DEBUG.enabled && this.after('trigger', registry.trigger);
215 this.after('teardown', {obj: registry, fnName: 'teardown'});