router updates

This commit is contained in:
Adam Bradley
2015-07-13 10:00:57 -05:00
parent 1427c254c5
commit 6576dd450e
13 changed files with 448 additions and 372 deletions

View File

@ -6,165 +6,189 @@ import {PathRecognizer} from './path-recognizer';
export class IonicRouter {
constructor(config) {
this._routes = {};
this._routes = [];
this._viewCtrls = [];
this.config(config);
}
app(app) {
this._app = app;
this.app = app;
}
config(config) {
if (config) {
for (let routeName in config) {
this.addRoute(routeName, config[routeName]);
for (let i = 0; i < config.length; i++) {
this.addRoute(config[i]);
}
}
}
addRoute(routeName, routeConfig) {
if (routeName && routeConfig && routeConfig.path) {
this._routes[routeName] = new Route(routeName, routeConfig);
addRoute(routeConfig) {
if (routeConfig && routeConfig.path && routeConfig.component) {
let route = new Route(routeConfig);
if (routeConfig.root) {
this.otherwise(routeName);
this.otherwise(route);
}
this._routes.push(route);
}
}
load(window, ionicApp, ionicConfig) {
// create each of the state manager classes
for (let name in stateManagerClasses) {
stateManagers[name] = new stateManagerClasses[name](window, this, ionicApp, ionicConfig);
}
stateManagerClasses = {};
return new Promise(resolve => {
this.getCurrentPath().then(path => {
this.loadByPath(path, this.otherwise()).then(resolve);
});
});
}
loadByPath(path, fallbackRoute) {
return new Promise(resolve => {
let self = this;
let activeViewCtrl = self.activeViewController();
let matchedRoute = self.match(path) || fallbackRoute;
function zoneLoad() {
self._app.zone().run(() => {
activeViewCtrl.push(matchedRoute.cls);
self.lastPath(matchedRoute.path);
resolve();
}, err => {
console.error(err);
});
}
if (activeViewCtrl && matchedRoute) {
if (matchedRoute.cls) {
zoneLoad();
} else if (matchedRoute.module) {
System.import(matchedRoute.module).then(m => {
if (m) {
matchedRoute.cls = m[matchedRoute.name];
zoneLoad();
}
}, err => {
console.error(err);
});
}
}
});
}
getCurrentPath() {
// check each of the state managers and the one with the
// highest priority wins of knowing what path we are currently at
return new Promise(resolve => {
let promises = [];
for (let name in stateManagers) {
promises.push(stateManagers[name].getCurrentPath());
}
// when all the promises have resolved then see which one wins
Promise.all(promises).then(results => {
let rtnPath = null;
let highestPriority = -1;
let state = null;
for (let i = 0; i < results.length; i++) {
state = results[i];
if (state.path && state.priority > highestPriority) {
rtnPath = state.path;
}
}
resolve(rtnPath);
});
});
}
stateChange(type, activeView) {
if (activeView && activeView.cls) {
let routeConfig = activeView.cls.route;
if (routeConfig) {
let matchedRoute = this.match(routeConfig.path);
if (matchedRoute) {
// this fires when the app's state has changed stateChange will
// tell each of the state managers that the state has changed, and
// each state manager will decide what to do with this info
// (the url state manager updates the url bar if a route was setup)
if (activeView && activeView.component) {
let componentRoute = activeView.component.route;
if (componentRoute) {
let path = componentRoute.generate(activeView.params);
if (path) {
for (let name in stateManagers) {
stateManagers[name].stateChange(matchedRoute.path, type, activeView);
stateManagers[name].stateChange(path, type, activeView);
}
}
}
this.lastPath(matchedRoute.path);
}
}
matchPaths(paths) {
// load each of paths to a component
let components = [];
let route;
if (paths) {
for (let i = 0; i < paths.length; i++) {
route = this.matchPath(paths[i]);
if (route && route.component) {
components.push(route.component);
}
}
}
return components;
}
lastPath(val) {
if (arguments.length) {
this._lastPath = val;
}
return this._lastPath;
}
match(path) {
matchPath(path) {
// takes a string path and loops through each of the setup
// routes to see if the path matches any of the routes
// the matched path with the highest specifity wins
let matchedRoute = null;
let route = null;
let routeMatch = null;
let highestSpecifity = 0;
for (let routeName in this._routes) {
routeMatch = this._routes[routeName].match(path);
for (let i = 0; i < this._routes.length; i++) {
route = this._routes[i];
routeMatch = route.match(path);
if (routeMatch.match && (!matchedRoute || routeMatch.specificity > highestSpecifity)) {
matchedRoute = this._routes[routeName];
highestSpecifity = routeMatch.specificity;
if (routeMatch && (!matchedRoute || route.specificity > matchedRoute.specificity)) {
matchedRoute = route;
}
}
return matchedRoute;
}
load(window, ionicApp, ionicConfig) {
// load is called when the app has finished loading each state
// manager gets a chance to say what path the app should be at
let viewCtrl = this.viewController();
if (!viewCtrl || !this._routes.length) {
return Promise.resolve();
}
let resolve;
let promise = new Promise(res => { resolve = res; });
// get the initial load paths from the state manager with the highest priorty
this.getManagerPaths(window, ionicApp, ionicConfig).then(paths => {
// load all of the paths the highest priority state manager has given
let components = this.matchPaths(paths);
if (!components.length && this.otherwise()) {
// the state manager did not find and loaded components
// use the "otherwise" path
components = [this.otherwise().component];
}
this.app.zoneRun(() => {
viewCtrl.setItems(components).then(resolve);
});
});
return promise;
}
getManagerPaths(window, ionicApp, ionicConfig) {
// loop through all of the state managers and load their paths
// the state manager with valid paths and highest priority wins
let resolve;
let promise = new Promise(res => { resolve = res; });
// load each of the state managers
let stateManagerPromises = [];
for (let name in stateManagerClasses) {
stateManagers[name] = new stateManagerClasses[name](window, this, ionicApp, ionicConfig);
stateManagerPromises.push( stateManagers[name].load() );
}
// when all the state manager loads have resolved then see which one wins
Promise.all(stateManagerPromises).then(stateManagerLoadResults => {
// now that all the state managers are loaded
// get the highest priority state manager's paths
let stateLoadResult = null;
let paths = null;
let highestPriority = -1;
for (let i = 0; i < stateManagerLoadResults.length; i++) {
stateLoadResult = stateManagerLoadResults[i];
if (stateLoadResult && stateLoadResult.paths.length && stateLoadResult.priority > highestPriority) {
paths = stateLoadResult.paths;
highestPriority = stateLoadResult.priority;
}
}
resolve(paths);
});
return promise;
}
push(path) {
let viewCtrl = this.viewController();
if (viewCtrl) {
let matchedRoute = this.matchPath(path);
if (matchedRoute && matchedRoute.component) {
this.app.zoneRun(() => {
viewCtrl.push(matchedRoute.component, matchedRoute.params, {});
});
}
}
}
pop() {
let viewCtrl = this.viewController();
if (viewCtrl) {
this.app.zoneRun(() => {
viewCtrl.pop();
});
}
}
otherwise(val) {
if (arguments.length) {
this._otherwise = val;
} else if (this._otherwise) {
return this._routes[this._otherwise];
}
return this._otherwise
}
addViewController(viewCtrl) {
this._viewCtrls.push(viewCtrl);
}
activeViewController() {
viewController() {
if (this._viewCtrls.length) {
return this._viewCtrls[ this._viewCtrls.length - 1 ];
}
@ -185,35 +209,21 @@ let stateManagerClasses = {};
let stateManagers = {};
export class Routable {
constructor(cls, routeConfig) {
cls.route = routeConfig;
}
}
class Route {
constructor(name, routeConfig) {
this.name = name;
this.cls = null;
constructor(routeConfig) {
util.extend(this, routeConfig);
this.recognizer = new PathRecognizer(this.path);
this.specificity = this.recognizer.specificity;
this.component.route = this;
}
match(matchPath) {
let routeMatch = new RouteMatch(this, matchPath);
if (routeMatch) {
return routeMatch;
}
return false;
match(path) {
return RegExpWrapper.firstMatch(this.recognizer.regex, path);
}
generate(params) {
return this.recognizer.generate(params);
}
}
class RouteMatch {
constructor(route, matchPath) {
this.route = route;
this.specificity = route.recognizer.specificity;
this.match = RegExpWrapper.firstMatch(route.recognizer.regex, matchPath);
}
}