director.js 20 KB

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