|
|
@@ -0,0 +1,719 @@
|
|
|
+
|
|
|
+
|
|
|
+//
|
|
|
+// Generated on Fri Dec 27 2013 12:02:11 GMT-0500 (EST) by Nodejitsu, Inc (Using Codesurgeon).
|
|
|
+// Version 1.2.2
|
|
|
+//
|
|
|
+
|
|
|
+(function (exports) {
|
|
|
+
|
|
|
+/*
|
|
|
+ * browser.js: Browser specific functionality for director.
|
|
|
+ *
|
|
|
+ * (C) 2011, Nodejitsu Inc.
|
|
|
+ * MIT LICENSE
|
|
|
+ *
|
|
|
+ */
|
|
|
+
|
|
|
+if (!Array.prototype.filter) {
|
|
|
+ Array.prototype.filter = function(filter, that) {
|
|
|
+ var other = [], v;
|
|
|
+ for (var i = 0, n = this.length; i < n; i++) {
|
|
|
+ if (i in this && filter.call(that, v = this[i], i, this)) {
|
|
|
+ other.push(v);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return other;
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+if (!Array.isArray){
|
|
|
+ Array.isArray = function(obj) {
|
|
|
+ return Object.prototype.toString.call(obj) === '[object Array]';
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+var dloc = document.location;
|
|
|
+
|
|
|
+function dlocHashEmpty() {
|
|
|
+ // Non-IE browsers return '' when the address bar shows '#'; Director's logic
|
|
|
+ // assumes both mean empty.
|
|
|
+ return dloc.hash === '' || dloc.hash === '#';
|
|
|
+}
|
|
|
+
|
|
|
+var listener = {
|
|
|
+ mode: 'modern',
|
|
|
+ hash: dloc.hash,
|
|
|
+ history: false,
|
|
|
+
|
|
|
+ check: function () {
|
|
|
+ var h = dloc.hash;
|
|
|
+ if (h != this.hash) {
|
|
|
+ this.hash = h;
|
|
|
+ this.onHashChanged();
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ fire: function () {
|
|
|
+ if (this.mode === 'modern') {
|
|
|
+ this.history === true ? window.onpopstate() : window.onhashchange();
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ this.onHashChanged();
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ init: function (fn, history) {
|
|
|
+ var self = this;
|
|
|
+ this.history = history;
|
|
|
+
|
|
|
+ if (!Router.listeners) {
|
|
|
+ Router.listeners = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ function onchange(onChangeEvent) {
|
|
|
+ for (var i = 0, l = Router.listeners.length; i < l; i++) {
|
|
|
+ Router.listeners[i](onChangeEvent);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //note IE8 is being counted as 'modern' because it has the hashchange event
|
|
|
+ if ('onhashchange' in window && (document.documentMode === undefined
|
|
|
+ || document.documentMode > 7)) {
|
|
|
+ // At least for now HTML5 history is available for 'modern' browsers only
|
|
|
+ if (this.history === true) {
|
|
|
+ // There is an old bug in Chrome that causes onpopstate to fire even
|
|
|
+ // upon initial page load. Since the handler is run manually in init(),
|
|
|
+ // this would cause Chrome to run it twise. Currently the only
|
|
|
+ // workaround seems to be to set the handler after the initial page load
|
|
|
+ // http://code.google.com/p/chromium/issues/detail?id=63040
|
|
|
+ setTimeout(function() {
|
|
|
+ window.onpopstate = onchange;
|
|
|
+ }, 500);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ window.onhashchange = onchange;
|
|
|
+ }
|
|
|
+ this.mode = 'modern';
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ //
|
|
|
+ // IE support, based on a concept by Erik Arvidson ...
|
|
|
+ //
|
|
|
+ var frame = document.createElement('iframe');
|
|
|
+ frame.id = 'state-frame';
|
|
|
+ frame.style.display = 'none';
|
|
|
+ document.body.appendChild(frame);
|
|
|
+ this.writeFrame('');
|
|
|
+
|
|
|
+ if ('onpropertychange' in document && 'attachEvent' in document) {
|
|
|
+ document.attachEvent('onpropertychange', function () {
|
|
|
+ if (event.propertyName === 'location') {
|
|
|
+ self.check();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ window.setInterval(function () { self.check(); }, 50);
|
|
|
+
|
|
|
+ this.onHashChanged = onchange;
|
|
|
+ this.mode = 'legacy';
|
|
|
+ }
|
|
|
+
|
|
|
+ Router.listeners.push(fn);
|
|
|
+
|
|
|
+ return this.mode;
|
|
|
+ },
|
|
|
+
|
|
|
+ destroy: function (fn) {
|
|
|
+ if (!Router || !Router.listeners) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ var listeners = Router.listeners;
|
|
|
+
|
|
|
+ for (var i = listeners.length - 1; i >= 0; i--) {
|
|
|
+ if (listeners[i] === fn) {
|
|
|
+ listeners.splice(i, 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ setHash: function (s) {
|
|
|
+ // Mozilla always adds an entry to the history
|
|
|
+ if (this.mode === 'legacy') {
|
|
|
+ this.writeFrame(s);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.history === true) {
|
|
|
+ window.history.pushState({}, document.title, s);
|
|
|
+ // Fire an onpopstate event manually since pushing does not obviously
|
|
|
+ // trigger the pop event.
|
|
|
+ this.fire();
|
|
|
+ } else {
|
|
|
+ dloc.hash = (s[0] === '/') ? s : '/' + s;
|
|
|
+ }
|
|
|
+ return this;
|
|
|
+ },
|
|
|
+
|
|
|
+ writeFrame: function (s) {
|
|
|
+ // IE support...
|
|
|
+ var f = document.getElementById('state-frame');
|
|
|
+ var d = f.contentDocument || f.contentWindow.document;
|
|
|
+ d.open();
|
|
|
+ d.write("<script>_hash = '" + s + "'; onload = parent.listener.syncHash;<script>");
|
|
|
+ d.close();
|
|
|
+ },
|
|
|
+
|
|
|
+ syncHash: function () {
|
|
|
+ // IE support...
|
|
|
+ var s = this._hash;
|
|
|
+ if (s != dloc.hash) {
|
|
|
+ dloc.hash = s;
|
|
|
+ }
|
|
|
+ return this;
|
|
|
+ },
|
|
|
+
|
|
|
+ onHashChanged: function () {}
|
|
|
+};
|
|
|
+
|
|
|
+var Router = exports.Router = function (routes) {
|
|
|
+ if (!(this instanceof Router)) return new Router(routes);
|
|
|
+
|
|
|
+ this.params = {};
|
|
|
+ this.routes = {};
|
|
|
+ this.methods = ['on', 'once', 'after', 'before'];
|
|
|
+ this.scope = [];
|
|
|
+ this._methods = {};
|
|
|
+
|
|
|
+ this._insert = this.insert;
|
|
|
+ this.insert = this.insertEx;
|
|
|
+
|
|
|
+ this.historySupport = (window.history != null ? window.history.pushState : null) != null
|
|
|
+
|
|
|
+ this.configure();
|
|
|
+ this.mount(routes || {});
|
|
|
+};
|
|
|
+
|
|
|
+Router.prototype.init = function (r) {
|
|
|
+ var self = this;
|
|
|
+ this.handler = function(onChangeEvent) {
|
|
|
+ var newURL = onChangeEvent && onChangeEvent.newURL || window.location.hash;
|
|
|
+ var url = self.history === true ? self.getPath() : newURL.replace(/.*#/, '');
|
|
|
+ self.dispatch('on', url.charAt(0) === '/' ? url : '/' + url);
|
|
|
+ };
|
|
|
+
|
|
|
+ listener.init(this.handler, this.history);
|
|
|
+
|
|
|
+ if (this.history === false) {
|
|
|
+ if (dlocHashEmpty() && r) {
|
|
|
+ dloc.hash = r;
|
|
|
+ } else if (!dlocHashEmpty()) {
|
|
|
+ self.dispatch('on', '/' + dloc.hash.replace(/^(#\/|#|\/)/, ''));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ var routeTo = dlocHashEmpty() && r ? r : !dlocHashEmpty() ? dloc.hash.replace(/^#/, '') : null;
|
|
|
+ if (routeTo) {
|
|
|
+ window.history.replaceState({}, document.title, routeTo);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Router has been initialized, but due to the chrome bug it will not
|
|
|
+ // yet actually route HTML5 history state changes. Thus, decide if should route.
|
|
|
+ if (routeTo || this.run_in_init === true) {
|
|
|
+ this.handler();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return this;
|
|
|
+};
|
|
|
+
|
|
|
+Router.prototype.explode = function () {
|
|
|
+ var v = this.history === true ? this.getPath() : dloc.hash;
|
|
|
+ if (v.charAt(1) === '/') { v=v.slice(1) }
|
|
|
+ return v.slice(1, v.length).split("/");
|
|
|
+};
|
|
|
+
|
|
|
+Router.prototype.setRoute = function (i, v, val) {
|
|
|
+ var url = this.explode();
|
|
|
+
|
|
|
+ if (typeof i === 'number' && typeof v === 'string') {
|
|
|
+ url[i] = v;
|
|
|
+ }
|
|
|
+ else if (typeof val === 'string') {
|
|
|
+ url.splice(i, v, s);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ url = [i];
|
|
|
+ }
|
|
|
+
|
|
|
+ listener.setHash(url.join('/'));
|
|
|
+ return url;
|
|
|
+};
|
|
|
+
|
|
|
+//
|
|
|
+// ### function insertEx(method, path, route, parent)
|
|
|
+// #### @method {string} Method to insert the specific `route`.
|
|
|
+// #### @path {Array} Parsed path to insert the `route` at.
|
|
|
+// #### @route {Array|function} Route handlers to insert.
|
|
|
+// #### @parent {Object} **Optional** Parent "routes" to insert into.
|
|
|
+// insert a callback that will only occur once per the matched route.
|
|
|
+//
|
|
|
+Router.prototype.insertEx = function(method, path, route, parent) {
|
|
|
+ if (method === "once") {
|
|
|
+ method = "on";
|
|
|
+ route = function(route) {
|
|
|
+ var once = false;
|
|
|
+ return function() {
|
|
|
+ if (once) return;
|
|
|
+ once = true;
|
|
|
+ return route.apply(this, arguments);
|
|
|
+ };
|
|
|
+ }(route);
|
|
|
+ }
|
|
|
+ return this._insert(method, path, route, parent);
|
|
|
+};
|
|
|
+
|
|
|
+Router.prototype.getRoute = function (v) {
|
|
|
+ var ret = v;
|
|
|
+
|
|
|
+ if (typeof v === "number") {
|
|
|
+ ret = this.explode()[v];
|
|
|
+ }
|
|
|
+ else if (typeof v === "string"){
|
|
|
+ var h = this.explode();
|
|
|
+ ret = h.indexOf(v);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ ret = this.explode();
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+};
|
|
|
+
|
|
|
+Router.prototype.destroy = function () {
|
|
|
+ listener.destroy(this.handler);
|
|
|
+ return this;
|
|
|
+};
|
|
|
+
|
|
|
+Router.prototype.getPath = function () {
|
|
|
+ var path = window.location.pathname;
|
|
|
+ if (path.substr(0, 1) !== '/') {
|
|
|
+ path = '/' + path;
|
|
|
+ }
|
|
|
+ return path;
|
|
|
+};
|
|
|
+function _every(arr, iterator) {
|
|
|
+ for (var i = 0; i < arr.length; i += 1) {
|
|
|
+ if (iterator(arr[i], i, arr) === false) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function _flatten(arr) {
|
|
|
+ var flat = [];
|
|
|
+ for (var i = 0, n = arr.length; i < n; i++) {
|
|
|
+ flat = flat.concat(arr[i]);
|
|
|
+ }
|
|
|
+ return flat;
|
|
|
+}
|
|
|
+
|
|
|
+function _asyncEverySeries(arr, iterator, callback) {
|
|
|
+ if (!arr.length) {
|
|
|
+ return callback();
|
|
|
+ }
|
|
|
+ var completed = 0;
|
|
|
+ (function iterate() {
|
|
|
+ iterator(arr[completed], function(err) {
|
|
|
+ if (err || err === false) {
|
|
|
+ callback(err);
|
|
|
+ callback = function() {};
|
|
|
+ } else {
|
|
|
+ completed += 1;
|
|
|
+ if (completed === arr.length) {
|
|
|
+ callback();
|
|
|
+ } else {
|
|
|
+ iterate();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ })();
|
|
|
+}
|
|
|
+
|
|
|
+function paramifyString(str, params, mod) {
|
|
|
+ mod = str;
|
|
|
+ for (var param in params) {
|
|
|
+ if (params.hasOwnProperty(param)) {
|
|
|
+ mod = params[param](str);
|
|
|
+ if (mod !== str) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return mod === str ? "([._a-zA-Z0-9-]+)" : mod;
|
|
|
+}
|
|
|
+
|
|
|
+function regifyString(str, params) {
|
|
|
+ var matches, last = 0, out = "";
|
|
|
+ while (matches = str.substr(last).match(/[^\w\d\- %@&]*\*[^\w\d\- %@&]*/)) {
|
|
|
+ last = matches.index + matches[0].length;
|
|
|
+ matches[0] = matches[0].replace(/^\*/, "([_.()!\\ %@&a-zA-Z0-9-]+)");
|
|
|
+ out += str.substr(0, matches.index) + matches[0];
|
|
|
+ }
|
|
|
+ str = out += str.substr(last);
|
|
|
+ var captures = str.match(/:([^\/]+)/ig), capture, length;
|
|
|
+ if (captures) {
|
|
|
+ length = captures.length;
|
|
|
+ for (var i = 0; i < length; i++) {
|
|
|
+ capture = captures[i];
|
|
|
+ if (capture.slice(0, 2) === "::") {
|
|
|
+ str = capture.slice(1);
|
|
|
+ } else {
|
|
|
+ str = str.replace(capture, paramifyString(capture, params));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return str;
|
|
|
+}
|
|
|
+
|
|
|
+function terminator(routes, delimiter, start, stop) {
|
|
|
+ var last = 0, left = 0, right = 0, start = (start || "(").toString(), stop = (stop || ")").toString(), i;
|
|
|
+ for (i = 0; i < routes.length; i++) {
|
|
|
+ var chunk = routes[i];
|
|
|
+ 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)) {
|
|
|
+ left = chunk.indexOf(start, last);
|
|
|
+ right = chunk.indexOf(stop, last);
|
|
|
+ if (~left && !~right || !~left && ~right) {
|
|
|
+ var tmp = routes.slice(0, (i || 1) + 1).join(delimiter);
|
|
|
+ routes = [ tmp ].concat(routes.slice((i || 1) + 1));
|
|
|
+ }
|
|
|
+ last = (right > left ? right : left) + 1;
|
|
|
+ i = 0;
|
|
|
+ } else {
|
|
|
+ last = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return routes;
|
|
|
+}
|
|
|
+
|
|
|
+Router.prototype.configure = function(options) {
|
|
|
+ options = options || {};
|
|
|
+ for (var i = 0; i < this.methods.length; i++) {
|
|
|
+ this._methods[this.methods[i]] = true;
|
|
|
+ }
|
|
|
+ this.recurse = options.recurse || this.recurse || false;
|
|
|
+ this.async = options.async || false;
|
|
|
+ this.delimiter = options.delimiter || "/";
|
|
|
+ this.strict = typeof options.strict === "undefined" ? true : options.strict;
|
|
|
+ this.notfound = options.notfound;
|
|
|
+ this.resource = options.resource;
|
|
|
+ this.history = options.html5history && this.historySupport || false;
|
|
|
+ this.run_in_init = this.history === true && options.run_handler_in_init !== false;
|
|
|
+ this.every = {
|
|
|
+ after: options.after || null,
|
|
|
+ before: options.before || null,
|
|
|
+ on: options.on || null
|
|
|
+ };
|
|
|
+ return this;
|
|
|
+};
|
|
|
+
|
|
|
+Router.prototype.param = function(token, matcher) {
|
|
|
+ if (token[0] !== ":") {
|
|
|
+ token = ":" + token;
|
|
|
+ }
|
|
|
+ var compiled = new RegExp(token, "g");
|
|
|
+ this.params[token] = function(str) {
|
|
|
+ return str.replace(compiled, matcher.source || matcher);
|
|
|
+ };
|
|
|
+};
|
|
|
+
|
|
|
+Router.prototype.on = Router.prototype.route = function(method, path, route) {
|
|
|
+ var self = this;
|
|
|
+ if (!route && typeof path == "function") {
|
|
|
+ route = path;
|
|
|
+ path = method;
|
|
|
+ method = "on";
|
|
|
+ }
|
|
|
+ if (Array.isArray(path)) {
|
|
|
+ return path.forEach(function(p) {
|
|
|
+ self.on(method, p, route);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if (path.source) {
|
|
|
+ path = path.source.replace(/\\\//ig, "/");
|
|
|
+ }
|
|
|
+ if (Array.isArray(method)) {
|
|
|
+ return method.forEach(function(m) {
|
|
|
+ self.on(m.toLowerCase(), path, route);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ path = path.split(new RegExp(this.delimiter));
|
|
|
+ path = terminator(path, this.delimiter);
|
|
|
+ this.insert(method, this.scope.concat(path), route);
|
|
|
+};
|
|
|
+
|
|
|
+Router.prototype.dispatch = function(method, path, callback) {
|
|
|
+ var self = this, fns = this.traverse(method, path, this.routes, ""), invoked = this._invoked, after;
|
|
|
+ this._invoked = true;
|
|
|
+ if (!fns || fns.length === 0) {
|
|
|
+ this.last = [];
|
|
|
+ if (typeof this.notfound === "function") {
|
|
|
+ this.invoke([ this.notfound ], {
|
|
|
+ method: method,
|
|
|
+ path: path
|
|
|
+ }, callback);
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (this.recurse === "forward") {
|
|
|
+ fns = fns.reverse();
|
|
|
+ }
|
|
|
+ function updateAndInvoke() {
|
|
|
+ self.last = fns.after;
|
|
|
+ self.invoke(self.runlist(fns), self, callback);
|
|
|
+ }
|
|
|
+ after = this.every && this.every.after ? [ this.every.after ].concat(this.last) : [ this.last ];
|
|
|
+ if (after && after.length > 0 && invoked) {
|
|
|
+ if (this.async) {
|
|
|
+ this.invoke(after, this, updateAndInvoke);
|
|
|
+ } else {
|
|
|
+ this.invoke(after, this);
|
|
|
+ updateAndInvoke();
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ updateAndInvoke();
|
|
|
+ return true;
|
|
|
+};
|
|
|
+
|
|
|
+Router.prototype.invoke = function(fns, thisArg, callback) {
|
|
|
+ var self = this;
|
|
|
+ var apply;
|
|
|
+ if (this.async) {
|
|
|
+ apply = function(fn, next) {
|
|
|
+ if (Array.isArray(fn)) {
|
|
|
+ return _asyncEverySeries(fn, apply, next);
|
|
|
+ } else if (typeof fn == "function") {
|
|
|
+ fn.apply(thisArg, fns.captures.concat(next));
|
|
|
+ }
|
|
|
+ };
|
|
|
+ _asyncEverySeries(fns, apply, function() {
|
|
|
+ if (callback) {
|
|
|
+ callback.apply(thisArg, arguments);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ apply = function(fn) {
|
|
|
+ if (Array.isArray(fn)) {
|
|
|
+ return _every(fn, apply);
|
|
|
+ } else if (typeof fn === "function") {
|
|
|
+ return fn.apply(thisArg, fns.captures || []);
|
|
|
+ } else if (typeof fn === "string" && self.resource) {
|
|
|
+ self.resource[fn].apply(thisArg, fns.captures || []);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ _every(fns, apply);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+Router.prototype.traverse = function(method, path, routes, regexp, filter) {
|
|
|
+ var fns = [], current, exact, match, next, that;
|
|
|
+ function filterRoutes(routes) {
|
|
|
+ if (!filter) {
|
|
|
+ return routes;
|
|
|
+ }
|
|
|
+ function deepCopy(source) {
|
|
|
+ var result = [];
|
|
|
+ for (var i = 0; i < source.length; i++) {
|
|
|
+ result[i] = Array.isArray(source[i]) ? deepCopy(source[i]) : source[i];
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ function applyFilter(fns) {
|
|
|
+ for (var i = fns.length - 1; i >= 0; i--) {
|
|
|
+ if (Array.isArray(fns[i])) {
|
|
|
+ applyFilter(fns[i]);
|
|
|
+ if (fns[i].length === 0) {
|
|
|
+ fns.splice(i, 1);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (!filter(fns[i])) {
|
|
|
+ fns.splice(i, 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ var newRoutes = deepCopy(routes);
|
|
|
+ newRoutes.matched = routes.matched;
|
|
|
+ newRoutes.captures = routes.captures;
|
|
|
+ newRoutes.after = routes.after.filter(filter);
|
|
|
+ applyFilter(newRoutes);
|
|
|
+ return newRoutes;
|
|
|
+ }
|
|
|
+ if (path === this.delimiter && routes[method]) {
|
|
|
+ next = [ [ routes.before, routes[method] ].filter(Boolean) ];
|
|
|
+ next.after = [ routes.after ].filter(Boolean);
|
|
|
+ next.matched = true;
|
|
|
+ next.captures = [];
|
|
|
+ return filterRoutes(next);
|
|
|
+ }
|
|
|
+ for (var r in routes) {
|
|
|
+ if (routes.hasOwnProperty(r) && (!this._methods[r] || this._methods[r] && typeof routes[r] === "object" && !Array.isArray(routes[r]))) {
|
|
|
+ current = exact = regexp + this.delimiter + r;
|
|
|
+ if (!this.strict) {
|
|
|
+ exact += "[" + this.delimiter + "]?";
|
|
|
+ }
|
|
|
+ match = path.match(new RegExp("^" + exact));
|
|
|
+ if (!match) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (match[0] && match[0] == path && routes[r][method]) {
|
|
|
+ next = [ [ routes[r].before, routes[r][method] ].filter(Boolean) ];
|
|
|
+ next.after = [ routes[r].after ].filter(Boolean);
|
|
|
+ next.matched = true;
|
|
|
+ next.captures = match.slice(1);
|
|
|
+ if (this.recurse && routes === this.routes) {
|
|
|
+ next.push([ routes.before, routes.on ].filter(Boolean));
|
|
|
+ next.after = next.after.concat([ routes.after ].filter(Boolean));
|
|
|
+ }
|
|
|
+ return filterRoutes(next);
|
|
|
+ }
|
|
|
+ next = this.traverse(method, path, routes[r], current);
|
|
|
+ if (next.matched) {
|
|
|
+ if (next.length > 0) {
|
|
|
+ fns = fns.concat(next);
|
|
|
+ }
|
|
|
+ if (this.recurse) {
|
|
|
+ fns.push([ routes[r].before, routes[r].on ].filter(Boolean));
|
|
|
+ next.after = next.after.concat([ routes[r].after ].filter(Boolean));
|
|
|
+ if (routes === this.routes) {
|
|
|
+ fns.push([ routes["before"], routes["on"] ].filter(Boolean));
|
|
|
+ next.after = next.after.concat([ routes["after"] ].filter(Boolean));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ fns.matched = true;
|
|
|
+ fns.captures = next.captures;
|
|
|
+ fns.after = next.after;
|
|
|
+ return filterRoutes(fns);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+};
|
|
|
+
|
|
|
+Router.prototype.insert = function(method, path, route, parent) {
|
|
|
+ var methodType, parentType, isArray, nested, part;
|
|
|
+ path = path.filter(function(p) {
|
|
|
+ return p && p.length > 0;
|
|
|
+ });
|
|
|
+ parent = parent || this.routes;
|
|
|
+ part = path.shift();
|
|
|
+ if (/\:|\*/.test(part) && !/\\d|\\w/.test(part)) {
|
|
|
+ part = regifyString(part, this.params);
|
|
|
+ }
|
|
|
+ if (path.length > 0) {
|
|
|
+ parent[part] = parent[part] || {};
|
|
|
+ return this.insert(method, path, route, parent[part]);
|
|
|
+ }
|
|
|
+ if (!part && !path.length && parent === this.routes) {
|
|
|
+ methodType = typeof parent[method];
|
|
|
+ switch (methodType) {
|
|
|
+ case "function":
|
|
|
+ parent[method] = [ parent[method], route ];
|
|
|
+ return;
|
|
|
+ case "object":
|
|
|
+ parent[method].push(route);
|
|
|
+ return;
|
|
|
+ case "undefined":
|
|
|
+ parent[method] = route;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ parentType = typeof parent[part];
|
|
|
+ isArray = Array.isArray(parent[part]);
|
|
|
+ if (parent[part] && !isArray && parentType == "object") {
|
|
|
+ methodType = typeof parent[part][method];
|
|
|
+ switch (methodType) {
|
|
|
+ case "function":
|
|
|
+ parent[part][method] = [ parent[part][method], route ];
|
|
|
+ return;
|
|
|
+ case "object":
|
|
|
+ parent[part][method].push(route);
|
|
|
+ return;
|
|
|
+ case "undefined":
|
|
|
+ parent[part][method] = route;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ } else if (parentType == "undefined") {
|
|
|
+ nested = {};
|
|
|
+ nested[method] = route;
|
|
|
+ parent[part] = nested;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ throw new Error("Invalid route context: " + parentType);
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+Router.prototype.extend = function(methods) {
|
|
|
+ var self = this, len = methods.length, i;
|
|
|
+ function extend(method) {
|
|
|
+ self._methods[method] = true;
|
|
|
+ self[method] = function() {
|
|
|
+ var extra = arguments.length === 1 ? [ method, "" ] : [ method ];
|
|
|
+ self.on.apply(self, extra.concat(Array.prototype.slice.call(arguments)));
|
|
|
+ };
|
|
|
+ }
|
|
|
+ for (i = 0; i < len; i++) {
|
|
|
+ extend(methods[i]);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+Router.prototype.runlist = function(fns) {
|
|
|
+ var runlist = this.every && this.every.before ? [ this.every.before ].concat(_flatten(fns)) : _flatten(fns);
|
|
|
+ if (this.every && this.every.on) {
|
|
|
+ runlist.push(this.every.on);
|
|
|
+ }
|
|
|
+ runlist.captures = fns.captures;
|
|
|
+ runlist.source = fns.source;
|
|
|
+ return runlist;
|
|
|
+};
|
|
|
+
|
|
|
+Router.prototype.mount = function(routes, path) {
|
|
|
+ if (!routes || typeof routes !== "object" || Array.isArray(routes)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ var self = this;
|
|
|
+ path = path || [];
|
|
|
+ if (!Array.isArray(path)) {
|
|
|
+ path = path.split(self.delimiter);
|
|
|
+ }
|
|
|
+ function insertOrMount(route, local) {
|
|
|
+ var rename = route, parts = route.split(self.delimiter), routeType = typeof routes[route], isRoute = parts[0] === "" || !self._methods[parts[0]], event = isRoute ? "on" : rename;
|
|
|
+ if (isRoute) {
|
|
|
+ rename = rename.slice((rename.match(new RegExp("^" + self.delimiter)) || [ "" ])[0].length);
|
|
|
+ parts.shift();
|
|
|
+ }
|
|
|
+ if (isRoute && routeType === "object" && !Array.isArray(routes[route])) {
|
|
|
+ local = local.concat(parts);
|
|
|
+ self.mount(routes[route], local);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (isRoute) {
|
|
|
+ local = local.concat(rename.split(self.delimiter));
|
|
|
+ local = terminator(local, self.delimiter);
|
|
|
+ }
|
|
|
+ self.insert(event, local, routes[route]);
|
|
|
+ }
|
|
|
+ for (var route in routes) {
|
|
|
+ if (routes.hasOwnProperty(route)) {
|
|
|
+ insertOrMount(route, path.slice(0));
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+}(typeof exports === "object" ? exports : window));
|