Speedometer: Update the Flight.js implementation to a more recent library version
[WebKit-https.git] / PerformanceTests / Speedometer / resources / todomvc / dependency-examples / flight / flight / node_modules / flight / lib / registry.js
1 /* Copyright 2013 Twitter, Inc. Licensed under The MIT License. http://opensource.org/licenses/MIT */
2
3 define(
4
5   [],
6
7   function() {
8     'use strict';
9
10     function parseEventArgs(instance, args) {
11       var element, type, callback;
12       var end = args.length;
13
14       if (typeof args[end - 1] === 'function') {
15         end -= 1;
16         callback = args[end];
17       }
18
19       if (typeof args[end - 1] === 'object') {
20         end -= 1;
21       }
22
23       if (end == 2) {
24         element = args[0];
25         type = args[1];
26       } else {
27         element = instance.node;
28         type = args[0];
29       }
30
31       return {
32         element: element,
33         type: type,
34         callback: callback
35       };
36     }
37
38     function matchEvent(a, b) {
39       return (
40         (a.element == b.element) &&
41         (a.type == b.type) &&
42         (b.callback == null || (a.callback == b.callback))
43       );
44     }
45
46     function Registry() {
47
48       var registry = this;
49
50       (this.reset = function() {
51         this.components = [];
52         this.allInstances = {};
53         this.events = [];
54       }).call(this);
55
56       function ComponentInfo(component) {
57         this.component = component;
58         this.attachedTo = [];
59         this.instances = {};
60
61         this.addInstance = function(instance) {
62           var instanceInfo = new InstanceInfo(instance);
63           this.instances[instance.identity] = instanceInfo;
64           this.attachedTo.push(instance.node);
65
66           return instanceInfo;
67         };
68
69         this.removeInstance = function(instance) {
70           delete this.instances[instance.identity];
71           var indexOfNode = this.attachedTo.indexOf(instance.node);
72           (indexOfNode > -1) && this.attachedTo.splice(indexOfNode, 1);
73
74           if (!Object.keys(this.instances).length) {
75             //if I hold no more instances remove me from registry
76             registry.removeComponentInfo(this);
77           }
78         };
79
80         this.isAttachedTo = function(node) {
81           return this.attachedTo.indexOf(node) > -1;
82         };
83       }
84
85       function InstanceInfo(instance) {
86         this.instance = instance;
87         this.events = [];
88
89         this.addBind = function(event) {
90           this.events.push(event);
91           registry.events.push(event);
92         };
93
94         this.removeBind = function(event) {
95           for (var i = 0, e; e = this.events[i]; i++) {
96             if (matchEvent(e, event)) {
97               this.events.splice(i, 1);
98             }
99           }
100         };
101       }
102
103       this.addInstance = function(instance) {
104         var component = this.findComponentInfo(instance);
105
106         if (!component) {
107           component = new ComponentInfo(instance.constructor);
108           this.components.push(component);
109         }
110
111         var inst = component.addInstance(instance);
112
113         this.allInstances[instance.identity] = inst;
114
115         return component;
116       };
117
118       this.removeInstance = function(instance) {
119         //remove from component info
120         var componentInfo = this.findComponentInfo(instance);
121         componentInfo && componentInfo.removeInstance(instance);
122
123         //remove from registry
124         delete this.allInstances[instance.identity];
125       };
126
127       this.removeComponentInfo = function(componentInfo) {
128         var index = this.components.indexOf(componentInfo);
129         (index > -1) && this.components.splice(index, 1);
130       };
131
132       this.findComponentInfo = function(which) {
133         var component = which.attachTo ? which : which.constructor;
134
135         for (var i = 0, c; c = this.components[i]; i++) {
136           if (c.component === component) {
137             return c;
138           }
139         }
140
141         return null;
142       };
143
144       this.findInstanceInfo = function(instance) {
145         return this.allInstances[instance.identity] || null;
146       };
147
148       this.getBoundEventNames = function(instance) {
149         return this.findInstanceInfo(instance).events.map(function(ev) {
150           return ev.type;
151         });
152       };
153
154       this.findInstanceInfoByNode = function(node) {
155         var result = [];
156         Object.keys(this.allInstances).forEach(function(k) {
157           var thisInstanceInfo = this.allInstances[k];
158           if (thisInstanceInfo.instance.node === node) {
159             result.push(thisInstanceInfo);
160           }
161         }, this);
162         return result;
163       };
164
165       this.on = function(componentOn) {
166         var instance = registry.findInstanceInfo(this), boundCallback;
167
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++) {
172           otherArgs[i - 1] = arguments[i];
173         }
174
175         if (instance) {
176           boundCallback = componentOn.apply(null, otherArgs);
177           if (boundCallback) {
178             otherArgs[otherArgs.length - 1] = boundCallback;
179           }
180           var event = parseEventArgs(this, otherArgs);
181           instance.addBind(event);
182         }
183       };
184
185       this.off = function(/*el, type, callback*/) {
186         var event = parseEventArgs(this, arguments),
187             instance = registry.findInstanceInfo(this);
188
189         if (instance) {
190           instance.removeBind(event);
191         }
192
193         //remove from global event registry
194         for (var i = 0, e; e = registry.events[i]; i++) {
195           if (matchEvent(e, event)) {
196             registry.events.splice(i, 1);
197           }
198         }
199       };
200
201       // debug tools may want to add advice to trigger
202       registry.trigger = function() {};
203
204       this.teardown = function() {
205         registry.removeInstance(this);
206       };
207
208       this.withRegistration = function() {
209         this.after('initialize', function() {
210           registry.addInstance(this);
211         });
212
213         this.around('on', registry.on);
214         this.after('off', registry.off);
215         //debug tools may want to add advice to trigger
216         window.DEBUG && DEBUG.enabled && this.after('trigger', registry.trigger);
217         this.after('teardown', {obj: registry, fnName: 'teardown'});
218       };
219
220     }
221
222     return new Registry;
223   }
224 );