DoYouEvenBench: Add Facebook's React TodoMVC test case
[WebKit-https.git] / PerformanceTests / DoYouEvenBench / todomvc / labs / architecture-examples / react / bower_components / director / build / director.js
1
2
3 //
4 // Generated on Sun Dec 16 2012 22:47:05 GMT-0500 (EST) by Nodejitsu, Inc (Using Codesurgeon).
5 // Version 1.1.9
6 //
7
8 (function (exports) {
9
10
11 /*
12  * browser.js: Browser specific functionality for director.
13  *
14  * (C) 2011, Nodejitsu Inc.
15  * MIT LICENSE
16  *
17  */
18
19 if (!Array.prototype.filter) {
20   Array.prototype.filter = function(filter, that) {
21     var other = [], v;
22     for (var i = 0, n = this.length; i < n; i++) {
23       if (i in this && filter.call(that, v = this[i], i, this)) {
24         other.push(v);
25       }
26     }
27     return other;
28   };
29 }
30
31 if (!Array.isArray){
32   Array.isArray = function(obj) {
33     return Object.prototype.toString.call(obj) === '[object Array]';
34   };
35 }
36
37 var dloc = document.location;
38
39 function dlocHashEmpty() {
40   // Non-IE browsers return '' when the address bar shows '#'; Director's logic
41   // assumes both mean empty.
42   return dloc.hash === '' || dloc.hash === '#';
43 }
44
45 var listener = {
46   mode: 'modern',
47   hash: dloc.hash,
48   history: false,
49
50   check: function () {
51     var h = dloc.hash;
52     if (h != this.hash) {
53       this.hash = h;
54       this.onHashChanged();
55     }
56   },
57
58   fire: function () {
59     if (this.mode === 'modern') {
60       this.history === true ? window.onpopstate() : window.onhashchange();
61     }
62     else {
63       this.onHashChanged();
64     }
65   },
66
67   init: function (fn, history) {
68     var self = this;
69     this.history = history;
70
71     if (!Router.listeners) {
72       Router.listeners = [];
73     }
74
75     function onchange(onChangeEvent) {
76       for (var i = 0, l = Router.listeners.length; i < l; i++) {
77         Router.listeners[i](onChangeEvent);
78       }
79     }
80
81     //note IE8 is being counted as 'modern' because it has the hashchange event
82     if ('onhashchange' in window && (document.documentMode === undefined
83       || document.documentMode > 7)) {
84       // At least for now HTML5 history is available for 'modern' browsers only
85       if (this.history === true) {
86         // There is an old bug in Chrome that causes onpopstate to fire even
87         // upon initial page load. Since the handler is run manually in init(),
88         // this would cause Chrome to run it twise. Currently the only
89         // workaround seems to be to set the handler after the initial page load
90         // http://code.google.com/p/chromium/issues/detail?id=63040
91         setTimeout(function() {
92           window.onpopstate = onchange;
93         }, 500);
94       }
95       else {
96         window.onhashchange = onchange;
97       }
98       this.mode = 'modern';
99     }
100     else {
101       //
102       // IE support, based on a concept by Erik Arvidson ...
103       //
104       var frame = document.createElement('iframe');
105       frame.id = 'state-frame';
106       frame.style.display = 'none';
107       document.body.appendChild(frame);
108       this.writeFrame('');
109
110       if ('onpropertychange' in document && 'attachEvent' in document) {
111         document.attachEvent('onpropertychange', function () {
112           if (event.propertyName === 'location') {
113             self.check();
114           }
115         });
116       }
117
118       window.setInterval(function () { self.check(); }, 50);
119
120       this.onHashChanged = onchange;
121       this.mode = 'legacy';
122     }
123
124     Router.listeners.push(fn);
125
126     return this.mode;
127   },
128
129   destroy: function (fn) {
130     if (!Router || !Router.listeners) {
131       return;
132     }
133
134     var listeners = Router.listeners;
135
136     for (var i = listeners.length - 1; i >= 0; i--) {
137       if (listeners[i] === fn) {
138         listeners.splice(i, 1);
139       }
140     }
141   },
142
143   setHash: function (s) {
144     // Mozilla always adds an entry to the history
145     if (this.mode === 'legacy') {
146       this.writeFrame(s);
147     }
148
149     if (this.history === true) {
150       window.history.pushState({}, document.title, s);
151       // Fire an onpopstate event manually since pushing does not obviously
152       // trigger the pop event.
153       this.fire();
154     } else {
155       dloc.hash = (s[0] === '/') ? s : '/' + s;
156     }
157     return this;
158   },
159
160   writeFrame: function (s) {
161     // IE support...
162     var f = document.getElementById('state-frame');
163     var d = f.contentDocument || f.contentWindow.document;
164     d.open();
165     d.write("<script>_hash = '" + s + "'; onload = parent.listener.syncHash;<script>");
166     d.close();
167   },
168
169   syncHash: function () {
170     // IE support...
171     var s = this._hash;
172     if (s != dloc.hash) {
173       dloc.hash = s;
174     }
175     return this;
176   },
177
178   onHashChanged: function () {}
179 };
180
181 var Router = exports.Router = function (routes) {
182   if (!(this instanceof Router)) return new Router(routes);
183
184   this.params   = {};
185   this.routes   = {};
186   this.methods  = ['on', 'once', 'after', 'before'];
187   this.scope    = [];
188   this._methods = {};
189
190   this._insert = this.insert;
191   this.insert = this.insertEx;
192
193   this.historySupport = (window.history != null ? window.history.pushState : null) != null
194
195   this.configure();
196   this.mount(routes || {});
197 };
198
199 Router.prototype.init = function (r) {
200   var self = this;
201   this.handler = function(onChangeEvent) {
202     var newURL = onChangeEvent && onChangeEvent.newURL || window.location.hash;
203     var url = self.history === true ? self.getPath() : newURL.replace(/.*#/, '');
204     self.dispatch('on', url);
205   };
206
207   listener.init(this.handler, this.history);
208
209   if (this.history === false) {
210     if (dlocHashEmpty() && r) {
211       dloc.hash = r;
212     } else if (!dlocHashEmpty()) {
213       self.dispatch('on', dloc.hash.replace(/^#/, ''));
214     }
215   }
216   else {
217     var routeTo = dlocHashEmpty() && r ? r : !dlocHashEmpty() ? dloc.hash.replace(/^#/, '') : null;
218     if (routeTo) {
219       window.history.replaceState({}, document.title, routeTo);
220     }
221
222     // Router has been initialized, but due to the chrome bug it will not
223     // yet actually route HTML5 history state changes. Thus, decide if should route.
224     if (routeTo || this.run_in_init === true) {
225       this.handler();
226     }
227   }
228
229   return this;
230 };
231
232 Router.prototype.explode = function () {
233   var v = this.history === true ? this.getPath() : dloc.hash;
234   if (v.charAt(1) === '/') { v=v.slice(1) }
235   return v.slice(1, v.length).split("/");
236 };
237
238 Router.prototype.setRoute = function (i, v, val) {
239   var url = this.explode();
240
241   if (typeof i === 'number' && typeof v === 'string') {
242     url[i] = v;
243   }
244   else if (typeof val === 'string') {
245     url.splice(i, v, s);
246   }
247   else {
248     url = [i];
249   }
250
251   listener.setHash(url.join('/'));
252   return url;
253 };
254
255 //
256 // ### function insertEx(method, path, route, parent)
257 // #### @method {string} Method to insert the specific `route`.
258 // #### @path {Array} Parsed path to insert the `route` at.
259 // #### @route {Array|function} Route handlers to insert.
260 // #### @parent {Object} **Optional** Parent "routes" to insert into.
261 // insert a callback that will only occur once per the matched route.
262 //
263 Router.prototype.insertEx = function(method, path, route, parent) {
264   if (method === "once") {
265     method = "on";
266     route = function(route) {
267       var once = false;
268       return function() {
269         if (once) return;
270         once = true;
271         return route.apply(this, arguments);
272       };
273     }(route);
274   }
275   return this._insert(method, path, route, parent);
276 };
277
278 Router.prototype.getRoute = function (v) {
279   var ret = v;
280
281   if (typeof v === "number") {
282     ret = this.explode()[v];
283   }
284   else if (typeof v === "string"){
285     var h = this.explode();
286     ret = h.indexOf(v);
287   }
288   else {
289     ret = this.explode();
290   }
291
292   return ret;
293 };
294
295 Router.prototype.destroy = function () {
296   listener.destroy(this.handler);
297   return this;
298 };
299
300 Router.prototype.getPath = function () {
301   var path = window.location.pathname;
302   if (path.substr(0, 1) !== '/') {
303     path = '/' + path;
304   }
305   return path;
306 };
307 function _every(arr, iterator) {
308   for (var i = 0; i < arr.length; i += 1) {
309     if (iterator(arr[i], i, arr) === false) {
310       return;
311     }
312   }
313 }
314
315 function _flatten(arr) {
316   var flat = [];
317   for (var i = 0, n = arr.length; i < n; i++) {
318     flat = flat.concat(arr[i]);
319   }
320   return flat;
321 }
322
323 function _asyncEverySeries(arr, iterator, callback) {
324   if (!arr.length) {
325     return callback();
326   }
327   var completed = 0;
328   (function iterate() {
329     iterator(arr[completed], function(err) {
330       if (err || err === false) {
331         callback(err);
332         callback = function() {};
333       } else {
334         completed += 1;
335         if (completed === arr.length) {
336           callback();
337         } else {
338           iterate();
339         }
340       }
341     });
342   })();
343 }
344
345 function paramifyString(str, params, mod) {
346   mod = str;
347   for (var param in params) {
348     if (params.hasOwnProperty(param)) {
349       mod = params[param](str);
350       if (mod !== str) {
351         break;
352       }
353     }
354   }
355   return mod === str ? "([._a-zA-Z0-9-]+)" : mod;
356 }
357
358 function regifyString(str, params) {
359   var matches, last = 0, out = "";
360   while (matches = str.substr(last).match(/[^\w\d\- %@&]*\*[^\w\d\- %@&]*/)) {
361     last = matches.index + matches[0].length;
362     matches[0] = matches[0].replace(/^\*/, "([_.()!\\ %@&a-zA-Z0-9-]+)");
363     out += str.substr(0, matches.index) + matches[0];
364   }
365   str = out += str.substr(last);
366   var captures = str.match(/:([^\/]+)/ig), length;
367   if (captures) {
368     length = captures.length;
369     for (var i = 0; i < length; i++) {
370       str = str.replace(captures[i], paramifyString(captures[i], params));
371     }
372   }
373   return str;
374 }
375
376 function terminator(routes, delimiter, start, stop) {
377   var last = 0, left = 0, right = 0, start = (start || "(").toString(), stop = (stop || ")").toString(), i;
378   for (i = 0; i < routes.length; i++) {
379     var chunk = routes[i];
380     if (chunk.indexOf(start, last) > chunk.indexOf(stop, last) || ~chunk.indexOf(start, last) && !~chunk.indexOf(stop, last) || !~chunk.indexOf(start, last) && ~chunk.indexOf(stop, last)) {
381       left = chunk.indexOf(start, last);
382       right = chunk.indexOf(stop, last);
383       if (~left && !~right || !~left && ~right) {
384         var tmp = routes.slice(0, (i || 1) + 1).join(delimiter);
385         routes = [ tmp ].concat(routes.slice((i || 1) + 1));
386       }
387       last = (right > left ? right : left) + 1;
388       i = 0;
389     } else {
390       last = 0;
391     }
392   }
393   return routes;
394 }
395
396 Router.prototype.configure = function(options) {
397   options = options || {};
398   for (var i = 0; i < this.methods.length; i++) {
399     this._methods[this.methods[i]] = true;
400   }
401   this.recurse = options.recurse || this.recurse || false;
402   this.async = options.async || false;
403   this.delimiter = options.delimiter || "/";
404   this.strict = typeof options.strict === "undefined" ? true : options.strict;
405   this.notfound = options.notfound;
406   this.resource = options.resource;
407   this.history = options.html5history && this.historySupport || false;
408   this.run_in_init = this.history === true && options.run_handler_in_init !== false;
409   this.every = {
410     after: options.after || null,
411     before: options.before || null,
412     on: options.on || null
413   };
414   return this;
415 };
416
417 Router.prototype.param = function(token, matcher) {
418   if (token[0] !== ":") {
419     token = ":" + token;
420   }
421   var compiled = new RegExp(token, "g");
422   this.params[token] = function(str) {
423     return str.replace(compiled, matcher.source || matcher);
424   };
425 };
426
427 Router.prototype.on = Router.prototype.route = function(method, path, route) {
428   var self = this;
429   if (!route && typeof path == "function") {
430     route = path;
431     path = method;
432     method = "on";
433   }
434   if (Array.isArray(path)) {
435     return path.forEach(function(p) {
436       self.on(method, p, route);
437     });
438   }
439   if (path.source) {
440     path = path.source.replace(/\\\//ig, "/");
441   }
442   if (Array.isArray(method)) {
443     return method.forEach(function(m) {
444       self.on(m.toLowerCase(), path, route);
445     });
446   }
447   path = path.split(new RegExp(this.delimiter));
448   path = terminator(path, this.delimiter);
449   this.insert(method, this.scope.concat(path), route);
450 };
451
452 Router.prototype.dispatch = function(method, path, callback) {
453   var self = this, fns = this.traverse(method, path, this.routes, ""), invoked = this._invoked, after;
454   this._invoked = true;
455   if (!fns || fns.length === 0) {
456     this.last = [];
457     if (typeof this.notfound === "function") {
458       this.invoke([ this.notfound ], {
459         method: method,
460         path: path
461       }, callback);
462     }
463     return false;
464   }
465   if (this.recurse === "forward") {
466     fns = fns.reverse();
467   }
468   function updateAndInvoke() {
469     self.last = fns.after;
470     self.invoke(self.runlist(fns), self, callback);
471   }
472   after = this.every && this.every.after ? [ this.every.after ].concat(this.last) : [ this.last ];
473   if (after && after.length > 0 && invoked) {
474     if (this.async) {
475       this.invoke(after, this, updateAndInvoke);
476     } else {
477       this.invoke(after, this);
478       updateAndInvoke();
479     }
480     return true;
481   }
482   updateAndInvoke();
483   return true;
484 };
485
486 Router.prototype.invoke = function(fns, thisArg, callback) {
487   var self = this;
488   if (this.async) {
489     _asyncEverySeries(fns, function apply(fn, next) {
490       if (Array.isArray(fn)) {
491         return _asyncEverySeries(fn, apply, next);
492       } else if (typeof fn == "function") {
493         fn.apply(thisArg, fns.captures.concat(next));
494       }
495     }, function() {
496       if (callback) {
497         callback.apply(thisArg, arguments);
498       }
499     });
500   } else {
501     _every(fns, function apply(fn) {
502       if (Array.isArray(fn)) {
503         return _every(fn, apply);
504       } else if (typeof fn === "function") {
505         return fn.apply(thisArg, fns.captures || []);
506       } else if (typeof fn === "string" && self.resource) {
507         self.resource[fn].apply(thisArg, fns.captures || []);
508       }
509     });
510   }
511 };
512
513 Router.prototype.traverse = function(method, path, routes, regexp, filter) {
514   var fns = [], current, exact, match, next, that;
515   function filterRoutes(routes) {
516     if (!filter) {
517       return routes;
518     }
519     function deepCopy(source) {
520       var result = [];
521       for (var i = 0; i < source.length; i++) {
522         result[i] = Array.isArray(source[i]) ? deepCopy(source[i]) : source[i];
523       }
524       return result;
525     }
526     function applyFilter(fns) {
527       for (var i = fns.length - 1; i >= 0; i--) {
528         if (Array.isArray(fns[i])) {
529           applyFilter(fns[i]);
530           if (fns[i].length === 0) {
531             fns.splice(i, 1);
532           }
533         } else {
534           if (!filter(fns[i])) {
535             fns.splice(i, 1);
536           }
537         }
538       }
539     }
540     var newRoutes = deepCopy(routes);
541     newRoutes.matched = routes.matched;
542     newRoutes.captures = routes.captures;
543     newRoutes.after = routes.after.filter(filter);
544     applyFilter(newRoutes);
545     return newRoutes;
546   }
547   if (path === this.delimiter && routes[method]) {
548     next = [ [ routes.before, routes[method] ].filter(Boolean) ];
549     next.after = [ routes.after ].filter(Boolean);
550     next.matched = true;
551     next.captures = [];
552     return filterRoutes(next);
553   }
554   for (var r in routes) {
555     if (routes.hasOwnProperty(r) && (!this._methods[r] || this._methods[r] && typeof routes[r] === "object" && !Array.isArray(routes[r]))) {
556       current = exact = regexp + this.delimiter + r;
557       if (!this.strict) {
558         exact += "[" + this.delimiter + "]?";
559       }
560       match = path.match(new RegExp("^" + exact));
561       if (!match) {
562         continue;
563       }
564       if (match[0] && match[0] == path && routes[r][method]) {
565         next = [ [ routes[r].before, routes[r][method] ].filter(Boolean) ];
566         next.after = [ routes[r].after ].filter(Boolean);
567         next.matched = true;
568         next.captures = match.slice(1);
569         if (this.recurse && routes === this.routes) {
570           next.push([ routes.before, routes.on ].filter(Boolean));
571           next.after = next.after.concat([ routes.after ].filter(Boolean));
572         }
573         return filterRoutes(next);
574       }
575       next = this.traverse(method, path, routes[r], current);
576       if (next.matched) {
577         if (next.length > 0) {
578           fns = fns.concat(next);
579         }
580         if (this.recurse) {
581           fns.push([ routes[r].before, routes[r].on ].filter(Boolean));
582           next.after = next.after.concat([ routes[r].after ].filter(Boolean));
583           if (routes === this.routes) {
584             fns.push([ routes["before"], routes["on"] ].filter(Boolean));
585             next.after = next.after.concat([ routes["after"] ].filter(Boolean));
586           }
587         }
588         fns.matched = true;
589         fns.captures = next.captures;
590         fns.after = next.after;
591         return filterRoutes(fns);
592       }
593     }
594   }
595   return false;
596 };
597
598 Router.prototype.insert = function(method, path, route, parent) {
599   var methodType, parentType, isArray, nested, part;
600   path = path.filter(function(p) {
601     return p && p.length > 0;
602   });
603   parent = parent || this.routes;
604   part = path.shift();
605   if (/\:|\*/.test(part) && !/\\d|\\w/.test(part)) {
606     part = regifyString(part, this.params);
607   }
608   if (path.length > 0) {
609     parent[part] = parent[part] || {};
610     return this.insert(method, path, route, parent[part]);
611   }
612   if (!part && !path.length && parent === this.routes) {
613     methodType = typeof parent[method];
614     switch (methodType) {
615      case "function":
616       parent[method] = [ parent[method], route ];
617       return;
618      case "object":
619       parent[method].push(route);
620       return;
621      case "undefined":
622       parent[method] = route;
623       return;
624     }
625     return;
626   }
627   parentType = typeof parent[part];
628   isArray = Array.isArray(parent[part]);
629   if (parent[part] && !isArray && parentType == "object") {
630     methodType = typeof parent[part][method];
631     switch (methodType) {
632      case "function":
633       parent[part][method] = [ parent[part][method], route ];
634       return;
635      case "object":
636       parent[part][method].push(route);
637       return;
638      case "undefined":
639       parent[part][method] = route;
640       return;
641     }
642   } else if (parentType == "undefined") {
643     nested = {};
644     nested[method] = route;
645     parent[part] = nested;
646     return;
647   }
648   throw new Error("Invalid route context: " + parentType);
649 };
650
651
652
653 Router.prototype.extend = function(methods) {
654   var self = this, len = methods.length, i;
655   function extend(method) {
656     self._methods[method] = true;
657     self[method] = function() {
658       var extra = arguments.length === 1 ? [ method, "" ] : [ method ];
659       self.on.apply(self, extra.concat(Array.prototype.slice.call(arguments)));
660     };
661   }
662   for (i = 0; i < len; i++) {
663     extend(methods[i]);
664   }
665 };
666
667 Router.prototype.runlist = function(fns) {
668   var runlist = this.every && this.every.before ? [ this.every.before ].concat(_flatten(fns)) : _flatten(fns);
669   if (this.every && this.every.on) {
670     runlist.push(this.every.on);
671   }
672   runlist.captures = fns.captures;
673   runlist.source = fns.source;
674   return runlist;
675 };
676
677 Router.prototype.mount = function(routes, path) {
678   if (!routes || typeof routes !== "object" || Array.isArray(routes)) {
679     return;
680   }
681   var self = this;
682   path = path || [];
683   if (!Array.isArray(path)) {
684     path = path.split(self.delimiter);
685   }
686   function insertOrMount(route, local) {
687     var rename = route, parts = route.split(self.delimiter), routeType = typeof routes[route], isRoute = parts[0] === "" || !self._methods[parts[0]], event = isRoute ? "on" : rename;
688     if (isRoute) {
689       rename = rename.slice((rename.match(new RegExp(self.delimiter)) || [ "" ])[0].length);
690       parts.shift();
691     }
692     if (isRoute && routeType === "object" && !Array.isArray(routes[route])) {
693       local = local.concat(parts);
694       self.mount(routes[route], local);
695       return;
696     }
697     if (isRoute) {
698       local = local.concat(rename.split(self.delimiter));
699       local = terminator(local, self.delimiter);
700     }
701     self.insert(event, local, routes[route]);
702   }
703   for (var route in routes) {
704     if (routes.hasOwnProperty(route)) {
705       insertOrMount(route, path.slice(0));
706     }
707   }
708 };
709
710
711
712 }(typeof exports === "object" ? exports : window));