mirror of
https://github.com/frappe/books.git
synced 2024-12-24 20:00:29 +00:00
140 lines
3.9 KiB
JavaScript
140 lines
3.9 KiB
JavaScript
const Observable = require('frappejs/utils/observable');
|
|
|
|
module.exports = class Router extends Observable {
|
|
constructor() {
|
|
super();
|
|
this.last_route = null;
|
|
this.current_page = null;
|
|
this.static_routes = [];
|
|
this.dynamic_routes = [];
|
|
}
|
|
|
|
add(route, handler) {
|
|
let page = {handler: handler, route: route};
|
|
|
|
// '/todo/:name/:place'.match(/:([^/]+)/g);
|
|
page.param_keys = route.match(/:([^/]+)/g);
|
|
|
|
if (page.param_keys) {
|
|
// make expression
|
|
// '/todo/:name/:place'.replace(/\/:([^/]+)/g, "\/([^/]+)");
|
|
page.depth = route.split('/').length;
|
|
page.expression = route.replace(/\/:([^/]+)/g, "\/([^/]+)");
|
|
this.dynamic_routes.push(page);
|
|
this.sort_dynamic_routes();
|
|
} else {
|
|
this.static_routes.push(page);
|
|
this.sort_static_routes();
|
|
}
|
|
}
|
|
|
|
sort_dynamic_routes() {
|
|
// routes with more parts first
|
|
this.dynamic_routes = this.dynamic_routes.sort((a, b) => {
|
|
if (a.depth < b.depth) {
|
|
return 1;
|
|
} else if (a.depth > b.depth) {
|
|
return -1;
|
|
} else {
|
|
if (a.param_keys.length !== b.param_keys.length) {
|
|
return a.param_keys.length > b.param_keys.length ? 1 : -1;
|
|
} else {
|
|
return a.route.length > b.route.length ? 1 : -1;
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
sort_static_routes() {
|
|
// longer routes on first
|
|
this.static_routes = this.static_routes.sort((a, b) => {
|
|
return a.route.length > b.route.length ? 1 : -1;
|
|
});
|
|
}
|
|
|
|
listen() {
|
|
window.addEventListener('hashchange', (event) => {
|
|
let route = this.getRoute_string();
|
|
if (this.last_route !== route) {
|
|
this.show(route);
|
|
}
|
|
});
|
|
}
|
|
|
|
// split and get routes
|
|
getRoute() {
|
|
let route = this.getRoute_string();
|
|
if (route) {
|
|
return route.split('/');
|
|
} else {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
async setRoute(...parts) {
|
|
const route = parts.join('/');
|
|
|
|
// setting this first, does not trigger show via hashchange,
|
|
// since we want to this with async/await, we need to trigger
|
|
// the show method
|
|
this.last_route = route;
|
|
|
|
window.location.hash = route;
|
|
await this.show(route);
|
|
}
|
|
|
|
async show(route) {
|
|
if (route && route[0]==='#') {
|
|
route = route.substr(1);
|
|
}
|
|
|
|
this.last_route = route;
|
|
|
|
if (!route) {
|
|
route = this.default;
|
|
}
|
|
let page = this.match(route);
|
|
|
|
if (page) {
|
|
if (typeof page.handler==='function') {
|
|
await page.handler(page.params);
|
|
} else {
|
|
await page.handler.show(page.params);
|
|
}
|
|
} else {
|
|
await this.match('not-found').handler({route: route});
|
|
}
|
|
await this.trigger('change');
|
|
}
|
|
|
|
match(route) {
|
|
// match static
|
|
for(let page of this.static_routes) {
|
|
if (page.route === route) {
|
|
return {handler: page.handler};
|
|
}
|
|
}
|
|
|
|
// match dynamic
|
|
for(let page of this.dynamic_routes) {
|
|
let matches = route.match(new RegExp(page.expression));
|
|
|
|
if (matches && matches.length == page.param_keys.length + 1) {
|
|
let params = {}
|
|
for (let i=0; i < page.param_keys.length; i++) {
|
|
params[page.param_keys[i].substr(1)] = matches[i + 1];
|
|
}
|
|
return {handler:page.handler, params: params};
|
|
}
|
|
}
|
|
}
|
|
|
|
getRoute_string() {
|
|
let route = window.location.hash;
|
|
if (route && route[0]==='#') {
|
|
route = route.substr(1);
|
|
}
|
|
return route;
|
|
}
|
|
}
|