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

@ -26,7 +26,11 @@ export class IonicApp {
load(appRef) {
this.ref(appRef);
this.zone(this.injector().get(NgZone));
this._zone = this.injector().get(NgZone);
}
title(val) {
document.title = val;
}
ref(val) {
@ -40,11 +44,8 @@ export class IonicApp {
return this._ref.injector;
}
zone(val) {
if (arguments.length) {
this._zone = val;
}
return this._zone;
zoneRun(fn) {
this._zone.run(fn);
}
stateChange(type, activeView) {
@ -74,10 +75,10 @@ export class IonicApp {
* Create and append the given component into the root
* element of the app.
*
* @param Component the cls to create and insert
* @param Component the component to create and insert
* @return Promise that resolves with the ContainerRef created
*/
appendComponent(cls: Type, context=null) {
appendComponent(component: Type, context=null) {
return new Promise((resolve, reject) => {
let injector = this.injector();
let compiler = injector.get(Compiler);
@ -85,7 +86,7 @@ export class IonicApp {
let rootComponentRef = this._ref._hostComponent;
let viewContainerLocation = rootComponentRef.location;
compiler.compileInHost(cls).then(protoViewRef => {
compiler.compileInHost(component).then(protoViewRef => {
let atIndex = 0;
let hostViewRef = viewMngr.createViewInContainer(
@ -158,8 +159,8 @@ function initApp(window, document, config) {
return app;
}
export function ionicBootstrap(cls, config, router) {
return new Promise((resolve, reject) => {
export function ionicBootstrap(component, config, router) {
return new Promise(resolve => {
try {
// get the user config, or create one if wasn't passed in
if (typeof config !== IonicConfig) {
@ -202,7 +203,7 @@ export function ionicBootstrap(cls, config, router) {
bind(Modal).toValue(modal)
];
bootstrap(cls, injectableBindings).then(appRef => {
bootstrap(component, injectableBindings).then(appRef => {
app.load(appRef);
router.load(window, app, config).then(() => {
@ -212,12 +213,10 @@ export function ionicBootstrap(cls, config, router) {
}).catch(err => {
console.error('ionicBootstrap', err);
reject(err);
});
} catch (err) {
console.error('ionicBootstrap', err);
reject(err);
console.error(err);
}
});
}

View File

@ -4,6 +4,7 @@ import {ProtoViewRef} from 'angular2/src/core/compiler/view_ref';
import {Ion} from '../ion';
import {IonicConfig} from '../../config/config';
import {IonicComponent} from '../../config/annotations';
import {IonicApp} from '../app/app';
import {ViewItem} from '../view/view-item';
import * as dom from '../../util/dom';
@ -42,9 +43,10 @@ import * as dom from '../../util/dom';
]
})
export class Navbar extends Ion {
constructor(item: ViewItem, elementRef: ElementRef, ionicConfig: IonicConfig) {
constructor(item: ViewItem, elementRef: ElementRef, ionicConfig: IonicConfig, app: IonicApp) {
super(elementRef, ionicConfig);
this.app = app;
this.eleRef = elementRef;
this.itemEles = [];
item.navbarView(this);
@ -130,8 +132,10 @@ export class Navbar extends Ion {
}
didEnter() {
setTimeout(() => {
const titleEle = this._ttEle || (this._ttEle = this.eleRef.nativeElement.querySelector('ion-title'));
this.app.title(titleEle.textContent);
setTimeout(() => {
//this.titleText((titleEle && titleEle.textContent) || '');
}, 32);
}

View File

@ -1,21 +1,25 @@
import {App} from 'ionic/ionic';
import {FirstPage} from './pages/first-page';
import {SecondPage} from './pages/second-page';
import {ThirdPage} from './pages/third-page';
@App({
routes: {
'FirstPage': {
'path': '/firstpage',
'module': 'dist/examples/nav/basic/pages/first-page',
'root': true
routes: [
{
path: '/firstpage',
component: FirstPage,
root: true
},
'SecondPage': {
'path': '/secondpage',
'module': 'dist/examples/nav/basic/pages/second-page',
},
'ThirdPage': {
'path': '/thirdpage',
'module': 'dist/examples/nav/basic/pages/third-page',
{
path: '/secondpage',
component: SecondPage,
},
{
path: '/thirdpage',
component: ThirdPage,
}
]
})
class MyApp {}

View File

@ -1,5 +1,5 @@
import {IonicView, IonicConfig, IonicApp} from 'ionic/ionic';
import {NavParams, Routable, NavController} from 'ionic/ionic';
import {NavParams, NavController} from 'ionic/ionic';
import {SecondPage} from './second-page';
import {ThirdPage} from './third-page';
@ -52,39 +52,35 @@ export class FirstPage {
this.nav.setItems(items);
}
viewLoaded() {
console.log('viewLoaded first page');
}
// viewLoaded() {
// console.log('viewLoaded first page');
// }
viewWillEnter() {
console.log('viewWillEnter first page');
}
// viewWillEnter() {
// console.log('viewWillEnter first page');
// }
viewDidEnter() {
console.log('viewDidEnter first page');
}
// viewDidEnter() {
// console.log('viewDidEnter first page');
// }
viewWillLeave() {
console.log('viewWillLeave first page');
}
// viewWillLeave() {
// console.log('viewWillLeave first page');
// }
viewDidLeave() {
console.log('viewDidLeave first page');
}
// viewDidLeave() {
// console.log('viewDidLeave first page');
// }
viewWillUnload() {
console.log('viewWillUnload first page');
}
// viewWillUnload() {
// console.log('viewWillUnload first page');
// }
viewDidUnload() {
console.log('viewDidUnload first page');
}
// viewDidUnload() {
// console.log('viewDidUnload first page');
// }
push() {
this.nav.push(SecondPage, { id: 8675309, myData: [1,2,3,4] }, { animation: 'ios' });
}
}
new Routable(FirstPage, {
path: '/firstpage'
});

View File

@ -1,4 +1,4 @@
import {IonicView, Routable, NavController, NavParams} from 'ionic/ionic';
import {IonicView, NavController, NavParams} from 'ionic/ionic';
import {ThirdPage} from './third-page';
import {FirstPage} from './first-page';
@ -45,36 +45,32 @@ export class SecondPage {
this.nav.push(ThirdPage);
}
viewLoaded() {
console.log('viewLoaded second page');
}
// viewLoaded() {
// console.log('viewLoaded second page');
// }
viewWillEnter() {
console.log('viewWillEnter second page');
}
// viewWillEnter() {
// console.log('viewWillEnter second page');
// }
viewDidEnter() {
console.log('viewDidEnter second page');
}
// viewDidEnter() {
// console.log('viewDidEnter second page');
// }
viewWillLeave() {
console.log('viewWillLeave second page');
}
// viewWillLeave() {
// console.log('viewWillLeave second page');
// }
viewDidLeave() {
console.log('viewDidLeave second page');
}
// viewDidLeave() {
// console.log('viewDidLeave second page');
// }
viewWillUnload() {
console.log('viewWillUnload second page');
}
// viewWillUnload() {
// console.log('viewWillUnload second page');
// }
viewDidUnload() {
console.log('viewDidUnload second page');
}
// viewDidUnload() {
// console.log('viewDidUnload second page');
// }
}
new Routable(SecondPage, {
path: '/secondpage'
});

View File

@ -1,4 +1,4 @@
import {IonicView, Routable, NavController} from 'ionic/ionic';
import {IonicView, NavController} from 'ionic/ionic';
@IonicView({
@ -23,36 +23,32 @@ export class ThirdPage {
this.nav.pop()
}
viewLoaded() {
console.log('viewLoaded third page');
}
// viewLoaded() {
// console.log('viewLoaded third page');
// }
viewWillEnter() {
console.log('viewWillEnter third page');
}
// viewWillEnter() {
// console.log('viewWillEnter third page');
// }
viewDidEnter() {
console.log('viewDidEnter third page');
}
// viewDidEnter() {
// console.log('viewDidEnter third page');
// }
viewWillLeave() {
console.log('viewWillLeave third page');
}
// viewWillLeave() {
// console.log('viewWillLeave third page');
// }
viewDidLeave() {
console.log('viewDidLeave third page');
}
// viewDidLeave() {
// console.log('viewDidLeave third page');
// }
viewWillUnload() {
console.log('viewWillUnload third page');
}
// viewWillUnload() {
// console.log('viewWillUnload third page');
// }
viewDidUnload() {
console.log('viewDidUnload third page');
}
// viewDidUnload() {
// console.log('viewDidUnload third page');
// }
}
new Routable(ThirdPage, {
path: '/thirdpage'
});

View File

@ -49,8 +49,8 @@ export class ViewController extends Ion {
]);
}
push(ComponentType, params = {}, opts = {}) {
if (!ComponentType || this.isTransitioning()) {
push(component, params = {}, opts = {}) {
if (!component || this.isTransitioning()) {
return Promise.reject();
}
@ -74,7 +74,7 @@ export class ViewController extends Ion {
}
// create a new ViewItem
let enteringItem = new ViewItem(this, ComponentType, params);
let enteringItem = new ViewItem(this, component, params);
// add the item to the stack
this.add(enteringItem);
@ -136,6 +136,10 @@ export class ViewController extends Ion {
* Set the item stack to reflect the given component classes.
*/
setItems(components, opts = {}) {
if (!components || !components.length) {
return Promise.resolve();
}
// if animate has not been set then default to false
opts.animate = opts.animate || false;
@ -162,6 +166,7 @@ export class ViewController extends Ion {
let newBeforeItems = components.slice(0, components.length - 1);
for (let j = 0; j < newBeforeItems.length; j++) {
component = newBeforeItems[j];
if (component) {
viewItem = new ViewItem(this, component.component || component, component.params);
viewItem.state = CACHED_STATE;
viewItem.shouldDestroy = false;
@ -171,19 +176,20 @@ export class ViewController extends Ion {
this.add(viewItem);
}
}
}
// get the component that will become the active item
// it'll be the last one in the given components array
component = components[ components.length - 1 ];
// transition the leaving and entering
return this.push(component.component || component, component.params, opts);
return this.push((component && component.component) || component, (component && component.params), opts);
}
setRoot(ComponentType, params = {}, opts = {}) {
setRoot(component, params = {}, opts = {}) {
return this.setItems([{
component: ComponentType,
params: params
component,
params
}], opts);
}
@ -485,7 +491,7 @@ export class ViewController extends Ion {
}
add(item) {
item.id = this.id + '' + (++this._ids);
item.id = this.id + '-' + (++this._ids);
this.items.push(item);
}

View File

@ -6,9 +6,9 @@ import {NavParams} from '../nav/nav-controller';
export class ViewItem {
constructor(viewCtrl, cls, params = {}) {
constructor(viewCtrl, component, params = {}) {
this.viewCtrl = viewCtrl;
this.cls = cls;
this.component = component;
this.params = new NavParams(params);
this.instance = null;
this.state = 0;
@ -37,7 +37,7 @@ export class ViewItem {
'class': 'nav-item'
}
});
let ionViewComponent = DirectiveBinding.createFromType(this.cls, annotation);
let ionViewComponent = DirectiveBinding.createFromType(this.component, annotation);
// compile the Component
viewCtrl.compiler.compileInHost(ionViewComponent).then(componentProtoViewRef => {

View File

@ -12,7 +12,7 @@ export * from 'ionic/platform/platform'
export * from 'ionic/platform/registry'
export * from 'ionic/routing/router'
export * from 'ionic/routing/hash-url-state'
export * from 'ionic/routing/url-state'
export * from 'ionic/util/click-block'
export * from 'ionic/util/focus'

View File

@ -1,105 +0,0 @@
import {IonicRouter} from './router';
import * as util from '../util/util';
class HashUrlStateManager {
constructor(window, router) {
this.location = window.location;
this.history = window.history;
this.router = router;
window.addEventListener('popstate', ev => {
this.onPopState(ev);
});
}
stateChange(path, type, activeView) {
if (type == 'pop') {
// if the popstate came from the browser's back button (and not Ionic)
// then we shouldn't force another browser history.back()
// only do a history.back() if the URL hasn't been updated yet
if (this.isDifferentPath(path)) {
this.history.back();
}
} else {
// push state change
let enteringState = {
path: path,
backPath: this.router.lastPath(),
forwardPath: null
};
if (this._hasInit) {
// update the leaving state to know what it's forward state will be
let leavingState = util.extend(this.history.state, {
forwardPath: enteringState.path
});
if (leavingState.path !== enteringState.path) {
this.history.replaceState(leavingState, '', '#' + leavingState.path);
}
if (this.isDifferentPath(path)) {
// push the new state to the history stack since the path
// isn't already in the location hash
this.history.pushState(enteringState, '', '#' + enteringState.path);
}
} else {
// replace the very first load with the correct entering state info
this.history.replaceState(enteringState, '', '#' + enteringState.path);
this._hasInit = true;
}
}
}
onPopState(ev) {
let newState = ev.state || {};
let newStatePath = newState.path;
let newStateBackPath = newState.backPath;
let newStateForwardPath = newState.forwardPath;
let lastLoadedStatePath = this.router.lastPath();
if (newStatePath === lastLoadedStatePath) {
// do nothing if the last path is the same
// as the "new" current state
return;
}
let activeViewCtrl = this.router.activeViewController();
if (activeViewCtrl) {
if (newStateForwardPath === lastLoadedStatePath) {
// if the last loaded state path is the same as the new
// state's forward path then the user is moving back
activeViewCtrl.pop();
} else if (newStateBackPath === lastLoadedStatePath) {
// if the last loaded state path is the new state's
// back path, then the user is moving forward
this.router.loadByPath(newStatePath);
}
}
}
getCurrentPath() {
// Grab the path without the leading hash
return new Promise(resolve => {
resolve({
path: this.location.hash.slice(1),
priority: 0
})
});
}
isDifferentPath(path) {
// check if the given path is different than the current location
return (this.location.hash !== ('#' + path));
}
}
IonicRouter.registerStateManager('hashurl', HashUrlStateManager);

View File

@ -30,8 +30,8 @@ class StaticSegment {
this.regex = escapeRegex(string);
}
generate(params) {
return this.string;
generate() {
return this.regex;
}
}
@ -49,7 +49,6 @@ class DynamicSegment {
}
}
class StarSegment {
constructor(name) {
this.regex = "(.+)";
@ -124,8 +123,6 @@ export class PathRecognizer {
constructor(path) {
this.segments = [];
// TODO: use destructuring assignment
// see https://github.com/angular/ts2dart/issues/158
var parsed = parsePathString(path);
var specificity = parsed['specificity'];
var segments = parsed['segments'];

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);
}
this.lastPath(matchedRoute.path);
}
stateManagers[name].stateChange(path, type, activeView);
}
}
}
lastPath(val) {
if (arguments.length) {
this._lastPath = val;
}
return this._lastPath;
}
match(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;
}
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;
match(path) {
return RegExpWrapper.firstMatch(this.recognizer.regex, path);
}
return false;
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);
}
}

173
ionic/routing/url-state.ts Normal file
View File

@ -0,0 +1,173 @@
import {IonicRouter} from './router';
import * as util from '../util/util';
class UrlStateManager {
constructor(window, router) {
this.location = window.location;
this.history = window.history;
this.ls = window.localStorage;
this.router = router;
// overkill for location change listeners, but ensures we
// know when the location has changed. Only 1 of the listeners
// will actually do the work, the other will be skipped.
window.addEventListener('popstate', () => {
this.onLocationChange();
});
window.addEventListener('hashchange', () => {
this.onLocationChange();
});
}
load() {
let paths = [this.getCurrentPath()];
let savedPaths = this.paths();
if (savedPaths[savedPaths.length - 1] == paths[0]) {
// the last path in the saved paths is the same as the
// current path, so use the saved paths to rebuild the history
paths = savedPaths;
} else {
// the current path is not the same as the last path in the
// saved history, so the saved history is no good, erase it
this.paths([]);
}
return Promise.resolve({
paths: paths,
priority: 0
});
}
stateChange(path, type, activeView) {
let savedPaths = this.paths();
// check if the given path is different than the current location
let isDifferentPath = (this.getCurrentPath() !== path);
if (type == 'pop') {
// if the popstate came from the browser's back button (and not Ionic)
// then we shouldn't force another browser history.back()
// only do a history.back() if the URL hasn't been updated yet
if (isDifferentPath) {
this.history.back();
}
if (savedPaths.length && savedPaths[savedPaths.length - 1] != path) {
// only if the last item in the saved paths
// equals this path then it can be removed
savedPaths.pop();
}
} else {
if (this._hasInit) {
if (isDifferentPath) {
// push the new state to the history stack since the path
// isn't already in the location hash
this.history.pushState(path, '', '#' + path);
}
} else {
// replace the very first load with the correct entering state info
this.history.replaceState(path, '', '#' + path);
this._hasInit = true;
}
if (savedPaths[savedPaths.length - 1] != path) {
// only if the last item in the saved paths does
// not equal this path then it can be added
savedPaths.push(path);
// don't allow the history to grow too large
if (savedPaths.length > MAX_PATH_STORE) {
savedPaths = savedPaths.slice( savedPaths.length - MAX_PATH_STORE );
}
}
}
// save the new path data
this.paths(savedPaths);
// ensure this resets
this._currentPath = null;
}
onLocationChange() {
let currentPath = this.getCurrentPath();
if (currentPath == this._currentPath) {
// absolutely no change since last onLocationChange
return;
}
// keep in-memory the current path to quickly tell if things have changed
this._currentPath = currentPath;
// load up the saved paths
let savedPaths = this.paths();
if (currentPath === savedPaths[savedPaths.length - 1]) {
// do nothing if the last saved path is
// the same as the current path
return;
}
if (currentPath === savedPaths[savedPaths.length - 2]) {
// the user is moving back
this.router.pop();
} else {
// the user is moving forward
this.router.push(currentPath);
}
}
paths(val) {
if (arguments.length) {
// set in-memory data
this._paths = val;
// set localStorage data
try {
this.ls.setItem(PATH_STORE_KEY, JSON.stringify(val));
} catch(e) {}
} else {
if (!this._paths) {
// we don't already have data in-memory
// see if we have data in localStorage
try {
let strData = this.ls.getItem(PATH_STORE_KEY);
if (strData) {
this._paths = JSON.parse(strData);
}
} catch(e) {}
// if not in localStorage yet then create new path data
if (!this._paths) {
this._paths = [];
}
}
// return the in-memory data
return this._paths;
}
}
getCurrentPath() {
// remove leading # to get the path
return this.location.hash.slice(1);
}
}
const PATH_STORE_KEY = 'ionic:history';
const MAX_PATH_STORE = 20;
IonicRouter.registerStateManager('url', UrlStateManager);