| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587 | 
							- Router = function () {
 
-   var self = this;
 
-   this.globals = [];
 
-   this.subscriptions = Function.prototype;
 
-   this._tracker = this._buildTracker();
 
-   this._current = {};
 
-   // tracks the current path change
 
-   this._onEveryPath = new Tracker.Dependency();
 
-   this._globalRoute = new Route(this);
 
-   // holds onRoute callbacks
 
-   this._onRouteCallbacks = [];
 
-   // if _askedToWait is true. We don't automatically start the router
 
-   // in Meteor.startup callback. (see client/_init.js)
 
-   // Instead user need to call `.initialize()
 
-   this._askedToWait = false;
 
-   this._initialized = false;
 
-   this._triggersEnter = [];
 
-   this._triggersExit = [];
 
-   this._routes = [];
 
-   this._routesMap = {};
 
-   this._updateCallbacks();
 
-   this.notFound = this.notfound = null;
 
-   // indicate it's okay (or not okay) to run the tracker
 
-   // when doing subscriptions
 
-   // using a number and increment it help us to support FlowRouter.go()
 
-   // and legitimate reruns inside tracker on the same event loop.
 
-   // this is a solution for #145
 
-   this.safeToRun = 0;
 
-   // Meteor exposes to the client the path prefix that was defined using the
 
-   // ROOT_URL environement variable on the server using the global runtime
 
-   // configuration. See #315.
 
-   this._basePath = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || '';
 
-   // this is a chain contains a list of old routes
 
-   // most of the time, there is only one old route
 
-   // but when it's the time for a trigger redirect we've a chain
 
-   this._oldRouteChain = [];
 
-   this.env = {
 
-     replaceState: new Meteor.EnvironmentVariable(),
 
-     reload: new Meteor.EnvironmentVariable(),
 
-     trailingSlash: new Meteor.EnvironmentVariable()
 
-   };
 
-   // redirect function used inside triggers
 
-   this._redirectFn = function(pathDef, fields, queryParams) {
 
-     if (/^http(s)?:\/\//.test(pathDef)) {
 
-         var message = "Redirects to URLs outside of the app are not supported in this version of Flow Router. Use 'window.location = yourUrl' instead";
 
-         throw new Error(message);
 
-     }
 
-     self.withReplaceState(function() {
 
-       var path = FlowRouter.path(pathDef, fields, queryParams);
 
-       self._page.redirect(path);
 
-     });
 
-   };
 
-   this._initTriggersAPI();
 
- };
 
- Router.prototype.route = function(pathDef, options, group) {
 
-   if (!/^\/.*/.test(pathDef)) {
 
-     var message = "route's path must start with '/'";
 
-     throw new Error(message);
 
-   }
 
-   options = options || {};
 
-   var self = this;
 
-   var route = new Route(this, pathDef, options, group);
 
-   // calls when the page route being activates
 
-   route._actionHandle = function (context, next) {
 
-     var oldRoute = self._current.route;
 
-     self._oldRouteChain.push(oldRoute);
 
-     var queryParams = self._qs.parse(context.querystring);
 
-     // _qs.parse() gives us a object without prototypes,
 
-     // created with Object.create(null)
 
-     // Meteor's check doesn't play nice with it.
 
-     // So, we need to fix it by cloning it.
 
-     // see more: https://github.com/meteorhacks/flow-router/issues/164
 
-     queryParams = JSON.parse(JSON.stringify(queryParams));
 
-     self._current = {
 
-       path: context.path,
 
-       context: context,
 
-       params: context.params,
 
-       queryParams: queryParams,
 
-       route: route,
 
-       oldRoute: oldRoute
 
-     };
 
-     // we need to invalidate if all the triggers have been completed
 
-     // if not that means, we've been redirected to another path
 
-     // then we don't need to invalidate
 
-     var afterAllTriggersRan = function() {
 
-       self._invalidateTracker();
 
-     };
 
-     var triggers = self._triggersEnter.concat(route._triggersEnter);
 
-     Triggers.runTriggers(
 
-       triggers,
 
-       self._current,
 
-       self._redirectFn,
 
-       afterAllTriggersRan
 
-     );
 
-   };
 
-   // calls when you exit from the page js route
 
-   route._exitHandle = function(context, next) {
 
-     var triggers = self._triggersExit.concat(route._triggersExit);
 
-     Triggers.runTriggers(
 
-       triggers,
 
-       self._current,
 
-       self._redirectFn,
 
-       next
 
-     );
 
-   };
 
-   this._routes.push(route);
 
-   if (options.name) {
 
-     this._routesMap[options.name] = route;
 
-   }
 
-   this._updateCallbacks();
 
-   this._triggerRouteRegister(route);
 
-   return route;
 
- };
 
- Router.prototype.group = function(options) {
 
-   return new Group(this, options);
 
- };
 
- Router.prototype.path = function(pathDef, fields, queryParams) {
 
-   if (this._routesMap[pathDef]) {
 
-     pathDef = this._routesMap[pathDef].pathDef;
 
-   }
 
-   var path = "";
 
-   // Prefix the path with the router global prefix
 
-   if (this._basePath) {
 
-     path += "/" + this._basePath + "/";
 
-   }
 
-   fields = fields || {};
 
-   var regExp = /(:[\w\(\)\\\+\*\.\?]+)+/g;
 
-   path += pathDef.replace(regExp, function(key) {
 
-     var firstRegexpChar = key.indexOf("(");
 
-     // get the content behind : and (\\d+/)
 
-     key = key.substring(1, (firstRegexpChar > 0)? firstRegexpChar: undefined);
 
-     // remove +?*
 
-     key = key.replace(/[\+\*\?]+/g, "");
 
-     // this is to allow page js to keep the custom characters as it is
 
-     // we need to encode 2 times otherwise "/" char does not work properly
 
-     // So, in that case, when I includes "/" it will think it's a part of the
 
-     // route. encoding 2times fixes it
 
-     return encodeURIComponent(encodeURIComponent(fields[key] || ""));
 
-   });
 
-   // Replace multiple slashes with single slash
 
-   path = path.replace(/\/\/+/g, "/");
 
-   // remove trailing slash
 
-   // but keep the root slash if it's the only one
 
-   path = path.match(/^\/{1}$/) ? path: path.replace(/\/$/, "");
 
-   // explictly asked to add a trailing slash
 
-   if(this.env.trailingSlash.get() && _.last(path) !== "/") {
 
-     path += "/";
 
-   }
 
-   var strQueryParams = this._qs.stringify(queryParams || {});
 
-   if(strQueryParams) {
 
-     path += "?" + strQueryParams;
 
-   }
 
-   return path;
 
- };
 
- Router.prototype.go = function(pathDef, fields, queryParams) {
 
-   var path = this.path(pathDef, fields, queryParams);
 
-   var useReplaceState = this.env.replaceState.get();
 
-   if(useReplaceState) {
 
-     this._page.replace(path);
 
-   } else {
 
-     this._page(path);
 
-   }
 
- };
 
- Router.prototype.reload = function() {
 
-   var self = this;
 
-   self.env.reload.withValue(true, function() {
 
-     self._page.replace(self._current.path);
 
-   });
 
- };
 
- Router.prototype.redirect = function(path) {
 
-   this._page.redirect(path);
 
- };
 
- Router.prototype.setParams = function(newParams) {
 
-   if(!this._current.route) {return false;}
 
-   var pathDef = this._current.route.pathDef;
 
-   var existingParams = this._current.params;
 
-   var params = {};
 
-   _.each(_.keys(existingParams), function(key) {
 
-     params[key] = existingParams[key];
 
-   });
 
-   params = _.extend(params, newParams);
 
-   var queryParams = this._current.queryParams;
 
-   this.go(pathDef, params, queryParams);
 
-   return true;
 
- };
 
- Router.prototype.setQueryParams = function(newParams) {
 
-   if(!this._current.route) {return false;}
 
-   var queryParams = _.clone(this._current.queryParams);
 
-   _.extend(queryParams, newParams);
 
-   for (var k in queryParams) {
 
-     if (queryParams[k] === null || queryParams[k] === undefined) {
 
-       delete queryParams[k];
 
-     }
 
-   }
 
-   var pathDef = this._current.route.pathDef;
 
-   var params = this._current.params;
 
-   this.go(pathDef, params, queryParams);
 
-   return true;
 
- };
 
- // .current is not reactive
 
- // This is by design. use .getParam() instead
 
- // If you really need to watch the path change, use .watchPathChange()
 
- Router.prototype.current = function() {
 
-   // We can't trust outside, that's why we clone this
 
-   // Anyway, we can't clone the whole object since it has non-jsonable values
 
-   // That's why we clone what's really needed.
 
-   var current = _.clone(this._current);
 
-   current.queryParams = EJSON.clone(current.queryParams);
 
-   current.params = EJSON.clone(current.params);
 
-   return current;
 
- };
 
- // Implementing Reactive APIs
 
- var reactiveApis = [
 
-   'getParam', 'getQueryParam',
 
-   'getRouteName', 'watchPathChange'
 
- ];
 
- reactiveApis.forEach(function(api) {
 
-   Router.prototype[api] = function(arg1) {
 
-     // when this is calling, there may not be any route initiated
 
-     // so we need to handle it
 
-     var currentRoute = this._current.route;
 
-     if(!currentRoute) {
 
-       this._onEveryPath.depend();
 
-       return;
 
-     }
 
-     // currently, there is only one argument. If we've more let's add more args
 
-     // this is not clean code, but better in performance
 
-     return currentRoute[api].call(currentRoute, arg1);
 
-   };
 
- });
 
- Router.prototype.subsReady = function() {
 
-   var callback = null;
 
-   var args = _.toArray(arguments);
 
-   if (typeof _.last(args) === "function") {
 
-     callback = args.pop();
 
-   }
 
-   var currentRoute = this.current().route;
 
-   var globalRoute = this._globalRoute;
 
-   // we need to depend for every route change and
 
-   // rerun subscriptions to check the ready state
 
-   this._onEveryPath.depend();
 
-   if(!currentRoute) {
 
-     return false;
 
-   }
 
-   var subscriptions;
 
-   if(args.length === 0) {
 
-     subscriptions = _.values(globalRoute.getAllSubscriptions());
 
-     subscriptions = subscriptions.concat(_.values(currentRoute.getAllSubscriptions()));
 
-   } else {
 
-     subscriptions = _.map(args, function(subName) {
 
-       return globalRoute.getSubscription(subName) || currentRoute.getSubscription(subName);
 
-     });
 
-   }
 
-   var isReady = function() {
 
-     var ready =  _.every(subscriptions, function(sub) {
 
-       return sub && sub.ready();
 
-     });
 
-     return ready;
 
-   };
 
-   if (callback) {
 
-     Tracker.autorun(function(c) {
 
-       if (isReady()) {
 
-         callback();
 
-         c.stop();
 
-       }
 
-     });
 
-   } else {
 
-     return isReady();
 
-   }
 
- };
 
- Router.prototype.withReplaceState = function(fn) {
 
-   return this.env.replaceState.withValue(true, fn);
 
- };
 
- Router.prototype.withTrailingSlash = function(fn) {
 
-   return this.env.trailingSlash.withValue(true, fn);
 
- };
 
- Router.prototype._notfoundRoute = function(context) {
 
-   this._current = {
 
-     path: context.path,
 
-     context: context,
 
-     params: [],
 
-     queryParams: {},
 
-   };
 
-   // XXX this.notfound kept for backwards compatibility
 
-   this.notFound = this.notFound || this.notfound;
 
-   if(!this.notFound) {
 
-     console.error("There is no route for the path:", context.path);
 
-     return;
 
-   }
 
-   this._current.route = new Route(this, "*", this.notFound);
 
-   this._invalidateTracker();
 
- };
 
- Router.prototype.initialize = function(options) {
 
-   options = options || {};
 
-   if(this._initialized) {
 
-     throw new Error("FlowRouter is already initialized");
 
-   }
 
-   var self = this;
 
-   this._updateCallbacks();
 
-   // Implementing idempotent routing
 
-   // by overriding page.js`s "show" method.
 
-   // Why?
 
-   // It is impossible to bypass exit triggers,
 
-   // because they execute before the handler and
 
-   // can not know what the next path is, inside exit trigger.
 
-   //
 
-   // we need override both show, replace to make this work
 
-   // since we use redirect when we are talking about withReplaceState
 
-   _.each(['show', 'replace'], function(fnName) {
 
-     var original = self._page[fnName];
 
-     self._page[fnName] = function(path, state, dispatch, push) {
 
-       var reload = self.env.reload.get();
 
-       if (!reload && self._current.path === path) {
 
-         return;
 
-       }
 
-       original.call(this, path, state, dispatch, push);
 
-     };
 
-   });
 
-   // this is very ugly part of pagejs and it does decoding few times
 
-   // in unpredicatable manner. See #168
 
-   // this is the default behaviour and we need keep it like that
 
-   // we are doing a hack. see .path()
 
-   this._page.base(this._basePath);
 
-   this._page({
 
-     decodeURLComponents: true,
 
-     hashbang: !!options.hashbang
 
-   });
 
-   this._initialized = true;
 
- };
 
- Router.prototype._buildTracker = function() {
 
-   var self = this;
 
-   // main autorun function
 
-   var tracker = Tracker.autorun(function () {
 
-     if(!self._current || !self._current.route) {
 
-       return;
 
-     }
 
-     // see the definition of `this._processingContexts`
 
-     var currentContext = self._current;
 
-     var route = currentContext.route;
 
-     var path = currentContext.path;
 
-     if(self.safeToRun === 0) {
 
-       var message =
 
-         "You can't use reactive data sources like Session" +
 
-         " inside the `.subscriptions` method!";
 
-       throw new Error(message);
 
-     }
 
-     // We need to run subscriptions inside a Tracker
 
-     // to stop subs when switching between routes
 
-     // But we don't need to run this tracker with
 
-     // other reactive changes inside the .subscription method
 
-     // We tackle this with the `safeToRun` variable
 
-     self._globalRoute.clearSubscriptions();
 
-     self.subscriptions.call(self._globalRoute, path);
 
-     route.callSubscriptions(currentContext);
 
-     // otherwise, computations inside action will trigger to re-run
 
-     // this computation. which we do not need.
 
-     Tracker.nonreactive(function() {
 
-       var isRouteChange = currentContext.oldRoute !== currentContext.route;
 
-       var isFirstRoute = !currentContext.oldRoute;
 
-       // first route is not a route change
 
-       if(isFirstRoute) {
 
-         isRouteChange = false;
 
-       }
 
-       // Clear oldRouteChain just before calling the action
 
-       // We still need to get a copy of the oldestRoute first
 
-       // It's very important to get the oldest route and registerRouteClose() it
 
-       // See: https://github.com/kadirahq/flow-router/issues/314
 
-       var oldestRoute = self._oldRouteChain[0];
 
-       self._oldRouteChain = [];
 
-       currentContext.route.registerRouteChange(currentContext, isRouteChange);
 
-       route.callAction(currentContext);
 
-       Tracker.afterFlush(function() {
 
-         self._onEveryPath.changed();
 
-         if(isRouteChange) {
 
-           // We need to trigger that route (definition itself) has changed.
 
-           // So, we need to re-run all the register callbacks to current route
 
-           // This is pretty important, otherwise tracker
 
-           // can't identify new route's items
 
-           // We also need to afterFlush, otherwise this will re-run
 
-           // helpers on templates which are marked for destroying
 
-           if(oldestRoute) {
 
-             oldestRoute.registerRouteClose();
 
-           }
 
-         }
 
-       });
 
-     });
 
-     self.safeToRun--;
 
-   });
 
-   return tracker;
 
- };
 
- Router.prototype._invalidateTracker = function() {
 
-   var self = this;
 
-   this.safeToRun++;
 
-   this._tracker.invalidate();
 
-   // After the invalidation we need to flush to make changes imediately
 
-   // otherwise, we have face some issues context mix-maches and so on.
 
-   // But there are some cases we can't flush. So we need to ready for that.
 
-   // we clearly know, we can't flush inside an autorun
 
-   // this may leads some issues on flow-routing
 
-   // we may need to do some warning
 
-   if(!Tracker.currentComputation) {
 
-     // Still there are some cases where we can't flush
 
-     //  eg:- when there is a flush currently
 
-     // But we've no public API or hacks to get that state
 
-     // So, this is the only solution
 
-     try {
 
-       Tracker.flush();
 
-     } catch(ex) {
 
-       // only handling "while flushing" errors
 
-       if(!/Tracker\.flush while flushing/.test(ex.message)) {
 
-         return;
 
-       }
 
-       // XXX: fix this with a proper solution by removing subscription mgt.
 
-       // from the router. Then we don't need to run invalidate using a tracker
 
-       // this happens when we are trying to invoke a route change
 
-       // with inside a route chnage. (eg:- Template.onCreated)
 
-       // Since we use page.js and tracker, we don't have much control
 
-       // over this process.
 
-       // only solution is to defer route execution.
 
-       // It's possible to have more than one path want to defer
 
-       // But, we only need to pick the last one.
 
-       // self._nextPath = self._current.path;
 
-       Meteor.defer(function() {
 
-         var path = self._nextPath;
 
-         if(!path) {
 
-           return;
 
-         }
 
-         delete self._nextPath;
 
-         self.env.reload.withValue(true, function() {
 
-           self.go(path);
 
-         });
 
-       });
 
-     }
 
-   }
 
- };
 
- Router.prototype._updateCallbacks = function () {
 
-   var self = this;
 
-   self._page.callbacks = [];
 
-   self._page.exits = [];
 
-   _.each(self._routes, function(route) {
 
-     self._page(route.pathDef, route._actionHandle);
 
-     self._page.exit(route.pathDef, route._exitHandle);
 
-   });
 
-   self._page("*", function(context) {
 
-     self._notfoundRoute(context);
 
-   });
 
- };
 
- Router.prototype._initTriggersAPI = function() {
 
-   var self = this;
 
-   this.triggers = {
 
-     enter: function(triggers, filter) {
 
-       triggers = Triggers.applyFilters(triggers, filter);
 
-       if(triggers.length) {
 
-         self._triggersEnter = self._triggersEnter.concat(triggers);
 
-       }
 
-     },
 
-     exit: function(triggers, filter) {
 
-       triggers = Triggers.applyFilters(triggers, filter);
 
-       if(triggers.length) {
 
-         self._triggersExit = self._triggersExit.concat(triggers);
 
-       }
 
-     }
 
-   };
 
- };
 
- Router.prototype.wait = function() {
 
-   if(this._initialized) {
 
-     throw new Error("can't wait after FlowRouter has been initialized");
 
-   }
 
-   this._askedToWait = true;
 
- };
 
- Router.prototype.onRouteRegister = function(cb) {
 
-   this._onRouteCallbacks.push(cb);
 
- };
 
- Router.prototype._triggerRouteRegister = function(currentRoute) {
 
-   // We should only need to send a safe set of fields on the route
 
-   // object.
 
-   // This is not to hide what's inside the route object, but to show
 
-   // these are the public APIs
 
-   var routePublicApi = _.pick(currentRoute, 'name', 'pathDef', 'path');
 
-   var omittingOptionFields = [
 
-     'triggersEnter', 'triggersExit', 'action', 'subscriptions', 'name'
 
-   ];
 
-   routePublicApi.options = _.omit(currentRoute.options, omittingOptionFields);
 
-   _.each(this._onRouteCallbacks, function(cb) {
 
-     cb(routePublicApi);
 
-   });
 
- };
 
- Router.prototype._page = page;
 
- Router.prototype._qs = qs;
 
 
  |