client/router: introduce own router
I'm tired of page.js lack of documentation around finer quirks, and being forced to read its crap code. Refactored into classes, removed unused cruft.
This commit is contained in:
parent
4295e1c827
commit
76882b59ef
19 changed files with 415 additions and 94 deletions
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const page = require('page');
|
||||
const router = require('../router.js');
|
||||
const api = require('../api.js');
|
||||
const events = require('../events.js');
|
||||
const topNavController = require('../controllers/top_nav_controller.js');
|
||||
|
@ -14,13 +14,20 @@ class AuthController {
|
|||
}
|
||||
|
||||
registerRoutes() {
|
||||
page(/\/password-reset\/([^:]+):([^:]+)$/,
|
||||
router.enter(
|
||||
/\/password-reset\/([^:]+):([^:]+)$/,
|
||||
(ctx, next) => {
|
||||
this._passwordResetFinishRoute(ctx.params[0], ctx.params[1]);
|
||||
});
|
||||
page('/password-reset', (ctx, next) => { this._passwordResetRoute(); });
|
||||
page('/login', (ctx, next) => { this._loginRoute(); });
|
||||
page('/logout', (ctx, next) => { this._logoutRoute(); });
|
||||
router.enter(
|
||||
'/password-reset',
|
||||
(ctx, next) => { this._passwordResetRoute(); });
|
||||
router.enter(
|
||||
'/login',
|
||||
(ctx, next) => { this._loginRoute(); });
|
||||
router.enter(
|
||||
'/logout',
|
||||
(ctx, next) => { this._logoutRoute(); });
|
||||
}
|
||||
|
||||
_loginRoute() {
|
||||
|
@ -33,7 +40,7 @@ class AuthController {
|
|||
api.login(name, password, doRemember)
|
||||
.then(() => {
|
||||
resolve();
|
||||
page('/');
|
||||
router.show('/');
|
||||
events.notify(events.Success, 'Logged in');
|
||||
}, errorMessage => {
|
||||
reject(errorMessage);
|
||||
|
@ -46,7 +53,7 @@ class AuthController {
|
|||
_logoutRoute() {
|
||||
api.forget();
|
||||
api.logout();
|
||||
page('/');
|
||||
router.show('/');
|
||||
events.notify(events.Success, 'Logged out');
|
||||
}
|
||||
|
||||
|
@ -68,10 +75,10 @@ class AuthController {
|
|||
}, response => {
|
||||
return Promise.reject(response.description);
|
||||
}).then(() => {
|
||||
page('/');
|
||||
router.show('/');
|
||||
events.notify(events.Success, 'New password: ' + password);
|
||||
}, errorMessage => {
|
||||
page('/');
|
||||
router.show('/');
|
||||
events.notify(events.Error, errorMessage);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const api = require('../api.js');
|
||||
const page = require('page');
|
||||
const router = require('../router.js');
|
||||
const misc = require('../util/misc.js');
|
||||
const topNavController = require('../controllers/top_nav_controller.js');
|
||||
const pageController = require('../controllers/page_controller.js');
|
||||
|
@ -10,7 +10,7 @@ const EmptyView = require('../views/empty_view.js');
|
|||
|
||||
class CommentsController {
|
||||
registerRoutes() {
|
||||
page('/comments/:query?',
|
||||
router.enter('/comments/:query?',
|
||||
(ctx, next) => { misc.parseSearchQueryRoute(ctx, next); },
|
||||
(ctx, next) => { this._listCommentsRoute(ctx); });
|
||||
this._commentsPageView = new CommentsPageView();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const page = require('page');
|
||||
const router = require('../router.js');
|
||||
const topNavController = require('../controllers/top_nav_controller.js');
|
||||
const HelpView = require('../views/help_view.js');
|
||||
|
||||
|
@ -10,11 +10,13 @@ class HelpController {
|
|||
}
|
||||
|
||||
registerRoutes() {
|
||||
page('/help', () => { this._showHelpRoute(); });
|
||||
page(
|
||||
router.enter(
|
||||
'/help',
|
||||
(ctx, next) => { this._showHelpRoute(); });
|
||||
router.enter(
|
||||
'/help/:section',
|
||||
(ctx, next) => { this._showHelpRoute(ctx.params.section); });
|
||||
page(
|
||||
router.enter(
|
||||
'/help/:section/:subsection',
|
||||
(ctx, next) => {
|
||||
this._showHelpRoute(ctx.params.section, ctx.params.subsection);
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
'use strict';
|
||||
|
||||
const page = require('page');
|
||||
const router = require('../router.js');
|
||||
const topNavController = require('../controllers/top_nav_controller.js');
|
||||
|
||||
class HistoryController {
|
||||
registerRoutes() {
|
||||
page('/history', (ctx, next) => { this._listHistoryRoute(); });
|
||||
router.enter(
|
||||
'/history',
|
||||
(ctx, next) => { this._listHistoryRoute(); });
|
||||
}
|
||||
|
||||
_listHistoryRoute() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const page = require('page');
|
||||
const router = require('../router.js');
|
||||
const api = require('../api.js');
|
||||
const events = require('../events.js');
|
||||
const topNavController = require('../controllers/top_nav_controller.js');
|
||||
|
@ -14,8 +14,12 @@ class HomeController {
|
|||
}
|
||||
|
||||
registerRoutes() {
|
||||
page('/', (ctx, next) => { this._indexRoute(); });
|
||||
page('*', (ctx, next) => { this._notFoundRoute(ctx); });
|
||||
router.enter(
|
||||
'/',
|
||||
(ctx, next) => { this._indexRoute(); });
|
||||
router.enter(
|
||||
'*',
|
||||
(ctx, next) => { this._notFoundRoute(ctx); });
|
||||
}
|
||||
|
||||
_indexRoute() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const page = require('page');
|
||||
const router = require('../router.js');
|
||||
const api = require('../api.js');
|
||||
const settings = require('../settings.js');
|
||||
const events = require('../events.js');
|
||||
|
@ -20,14 +20,17 @@ class PostsController {
|
|||
}
|
||||
|
||||
registerRoutes() {
|
||||
page('/upload', (ctx, next) => { this._uploadPostsRoute(); });
|
||||
page('/posts/:query?',
|
||||
router.enter(
|
||||
'/upload',
|
||||
(ctx, next) => { this._uploadPostsRoute(); });
|
||||
router.enter(
|
||||
'/posts/:query?',
|
||||
(ctx, next) => { misc.parseSearchQueryRoute(ctx, next); },
|
||||
(ctx, next) => { this._listPostsRoute(ctx); });
|
||||
page(
|
||||
router.enter(
|
||||
'/post/:id',
|
||||
(ctx, next) => { this._showPostRoute(ctx.params.id, false); });
|
||||
page(
|
||||
router.enter(
|
||||
'/post/:id/edit',
|
||||
(ctx, next) => { this._showPostRoute(ctx.params.id, true); });
|
||||
this._emptyView = new EmptyView();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const page = require('page');
|
||||
const router = require('../router.js');
|
||||
const settings = require('../settings.js');
|
||||
const topNavController = require('../controllers/top_nav_controller.js');
|
||||
const SettingsView = require('../views/settings_view.js');
|
||||
|
@ -11,7 +11,7 @@ class SettingsController {
|
|||
}
|
||||
|
||||
registerRoutes() {
|
||||
page('/settings', (ctx, next) => { this._settingsRoute(); });
|
||||
router.enter('/settings', (ctx, next) => { this._settingsRoute(); });
|
||||
}
|
||||
|
||||
_settingsRoute() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const page = require('page');
|
||||
const router = require('../router.js');
|
||||
const api = require('../api.js');
|
||||
const tags = require('../tags.js');
|
||||
const events = require('../events.js');
|
||||
|
@ -23,20 +23,22 @@ class TagsController {
|
|||
}
|
||||
|
||||
registerRoutes() {
|
||||
page('/tag-categories', () => { this._tagCategoriesRoute(); });
|
||||
page(
|
||||
router.enter(
|
||||
'/tag-categories',
|
||||
(ctx, next) => { this._tagCategoriesRoute(ctx, next); });
|
||||
router.enter(
|
||||
'/tag/:name',
|
||||
(ctx, next) => { this._loadTagRoute(ctx, next); },
|
||||
(ctx, next) => { this._showTagRoute(ctx, next); });
|
||||
page(
|
||||
router.enter(
|
||||
'/tag/:name/merge',
|
||||
(ctx, next) => { this._loadTagRoute(ctx, next); },
|
||||
(ctx, next) => { this._mergeTagRoute(ctx, next); });
|
||||
page(
|
||||
router.enter(
|
||||
'/tag/:name/delete',
|
||||
(ctx, next) => { this._loadTagRoute(ctx, next); },
|
||||
(ctx, next) => { this._deleteTagRoute(ctx, next); });
|
||||
page(
|
||||
router.enter(
|
||||
'/tags/:query?',
|
||||
(ctx, next) => { misc.parseSearchQueryRoute(ctx, next); },
|
||||
(ctx, next) => { this._listTagsRoute(ctx, next); });
|
||||
|
@ -136,7 +138,7 @@ class TagsController {
|
|||
_saveTag(tag, input) {
|
||||
return api.put('/tag/' + tag.names[0], input).then(response => {
|
||||
if (input.names && input.names[0] !== tag.names[0]) {
|
||||
page('/tag/' + input.names[0]);
|
||||
router.show('/tag/' + input.names[0]);
|
||||
}
|
||||
events.notify(events.Success, 'Tag saved.');
|
||||
return Promise.resolve();
|
||||
|
@ -151,7 +153,7 @@ class TagsController {
|
|||
'/tag-merge/',
|
||||
{remove: tag.names[0], mergeTo: targetTagName}
|
||||
).then(response => {
|
||||
page('/tag/' + targetTagName + '/merge');
|
||||
router.show('/tag/' + targetTagName + '/merge');
|
||||
events.notify(events.Success, 'Tag merged.');
|
||||
return Promise.resolve();
|
||||
}, response => {
|
||||
|
@ -162,7 +164,7 @@ class TagsController {
|
|||
|
||||
_deleteTag(tag) {
|
||||
return api.delete('/tag/' + tag.names[0]).then(response => {
|
||||
page('/tags/');
|
||||
router.show('/tags/');
|
||||
events.notify(events.Success, 'Tag deleted.');
|
||||
return Promise.resolve();
|
||||
}, response => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const page = require('page');
|
||||
const router = require('../router.js');
|
||||
const api = require('../api.js');
|
||||
const config = require('../config.js');
|
||||
const events = require('../events.js');
|
||||
|
@ -34,28 +34,31 @@ class UsersController {
|
|||
}
|
||||
|
||||
registerRoutes() {
|
||||
page('/register', () => { this._createUserRoute(); });
|
||||
page(
|
||||
router.enter(
|
||||
'/register',
|
||||
(ctx, next) => { this._createUserRoute(ctx, next); });
|
||||
router.enter(
|
||||
'/users/:query?',
|
||||
(ctx, next) => { misc.parseSearchQueryRoute(ctx, next); },
|
||||
(ctx, next) => { this._listUsersRoute(ctx, next); });
|
||||
page(
|
||||
router.enter(
|
||||
'/user/:name',
|
||||
(ctx, next) => { this._loadUserRoute(ctx, next); },
|
||||
(ctx, next) => { this._showUserRoute(ctx, next); });
|
||||
page(
|
||||
router.enter(
|
||||
'/user/:name/edit',
|
||||
(ctx, next) => { this._loadUserRoute(ctx, next); },
|
||||
(ctx, next) => { this._editUserRoute(ctx, next); });
|
||||
page(
|
||||
router.enter(
|
||||
'/user/:name/delete',
|
||||
(ctx, next) => { this._loadUserRoute(ctx, next); },
|
||||
(ctx, next) => { this._deleteUserRoute(ctx, next); });
|
||||
page.exit(/\/users\/.*/, (ctx, next) => {
|
||||
pageController.stop();
|
||||
next();
|
||||
});
|
||||
page.exit(/\/user\/.*/, (ctx, next) => {
|
||||
router.exit(
|
||||
/\/users\/.*/, (ctx, next) => {
|
||||
pageController.stop();
|
||||
next();
|
||||
});
|
||||
router.exit(/\/user\/.*/, (ctx, next) => {
|
||||
this._cachedUser = null;
|
||||
next();
|
||||
});
|
||||
|
@ -81,7 +84,7 @@ class UsersController {
|
|||
});
|
||||
}
|
||||
|
||||
_createUserRoute() {
|
||||
_createUserRoute(ctx, next) {
|
||||
topNavController.activate('register');
|
||||
this._registrationView.render({
|
||||
register: (...args) => {
|
||||
|
@ -135,7 +138,7 @@ class UsersController {
|
|||
return Promise.reject(response.description);
|
||||
}).then(() => {
|
||||
resolve();
|
||||
page('/');
|
||||
router.show('/');
|
||||
events.notify(events.Success, 'Welcome aboard!');
|
||||
}, errorMessage => {
|
||||
reject();
|
||||
|
@ -184,7 +187,7 @@ class UsersController {
|
|||
}).then(() => {
|
||||
resolve();
|
||||
if (data.name && data.name !== user.name) {
|
||||
page('/user/' + data.name + '/edit');
|
||||
router.show('/user/' + data.name + '/edit');
|
||||
}
|
||||
events.notify(events.Success, 'Settings updated.');
|
||||
}, errorMessage => {
|
||||
|
@ -203,9 +206,9 @@ class UsersController {
|
|||
api.logout();
|
||||
}
|
||||
if (api.hasPrivilege('users:list')) {
|
||||
page('/users');
|
||||
router.show('/users');
|
||||
} else {
|
||||
page('/');
|
||||
router.show('/');
|
||||
}
|
||||
events.notify(events.Success, 'Account deleted.');
|
||||
return Promise.resolve();
|
||||
|
|
|
@ -3,32 +3,37 @@
|
|||
require('./util/polyfill.js');
|
||||
const misc = require('./util/misc.js');
|
||||
|
||||
const page = require('page');
|
||||
const origPushState = page.Context.prototype.pushState;
|
||||
page.Context.prototype.pushState = function() {
|
||||
const router = require('./router.js');
|
||||
|
||||
const origPushState = router.Context.prototype.pushState;
|
||||
router.Context.prototype.pushState = function() {
|
||||
window.scrollTo(0, 0);
|
||||
origPushState.call(this);
|
||||
};
|
||||
|
||||
page.cancel = function(ctx) {
|
||||
router.cancel = function(ctx) {
|
||||
prevContext = ctx;
|
||||
ctx.pushState();
|
||||
};
|
||||
|
||||
page.exit((ctx, next) => {
|
||||
views.unlistenToMessages();
|
||||
if (misc.confirmPageExit()) {
|
||||
next();
|
||||
} else {
|
||||
page.cancel(ctx);
|
||||
}
|
||||
});
|
||||
router.exit(
|
||||
/.*/,
|
||||
(ctx, next) => {
|
||||
views.unlistenToMessages();
|
||||
if (misc.confirmPageExit()) {
|
||||
next();
|
||||
} else {
|
||||
router.cancel(ctx);
|
||||
}
|
||||
});
|
||||
|
||||
const mousetrap = require('mousetrap');
|
||||
page(/.*/, (ctx, next) => {
|
||||
mousetrap.reset();
|
||||
next();
|
||||
});
|
||||
router.enter(
|
||||
/.*/,
|
||||
(ctx, next) => {
|
||||
mousetrap.reset();
|
||||
next();
|
||||
});
|
||||
|
||||
let controllers = [];
|
||||
controllers.push(require('./controllers/auth_controller.js'));
|
||||
|
@ -40,6 +45,7 @@ controllers.push(require('./controllers/history_controller.js'));
|
|||
controllers.push(require('./controllers/tags_controller.js'));
|
||||
controllers.push(require('./controllers/settings_controller.js'));
|
||||
|
||||
// home defines 404 routes, need to be registered as last
|
||||
controllers.push(require('./controllers/home_controller.js'));
|
||||
|
||||
const tags = require('./tags.js');
|
||||
|
@ -52,13 +58,13 @@ for (let controller of controllers) {
|
|||
const api = require('./api.js');
|
||||
Promise.all([tags.refreshExport(), api.loginFromCookies()])
|
||||
.then(() => {
|
||||
page();
|
||||
router.start();
|
||||
}).catch(errorMessage => {
|
||||
if (window.location.href.indexOf('login') !== -1) {
|
||||
api.forget();
|
||||
page();
|
||||
router.start();
|
||||
} else {
|
||||
page('/');
|
||||
router.start('/');
|
||||
events.notify(
|
||||
events.Error,
|
||||
'An error happened while trying to log you in: ' +
|
||||
|
|
293
client/js/router.js
Normal file
293
client/js/router.js
Normal file
|
@ -0,0 +1,293 @@
|
|||
'use strict';
|
||||
|
||||
// modified page.js by visionmedia
|
||||
// - removed unused crap
|
||||
// - refactored to classes
|
||||
|
||||
const pathToRegexp = require('path-to-regexp');
|
||||
const clickEvent = document.ontouchstart ? 'touchstart' : 'click';
|
||||
let location = window.history.location || window.location;
|
||||
|
||||
const base = '';
|
||||
let prevContext = null;
|
||||
|
||||
function _decodeURLEncodedURIComponent(val) {
|
||||
if (typeof val !== 'string') {
|
||||
return val;
|
||||
}
|
||||
return decodeURIComponent(val.replace(/\+/g, ' '));
|
||||
}
|
||||
|
||||
function _isSameOrigin(href) {
|
||||
let origin = location.protocol + '//' + location.hostname;
|
||||
if (location.port) {
|
||||
origin += ':' + location.port;
|
||||
}
|
||||
return href && href.indexOf(origin) === 0;
|
||||
}
|
||||
|
||||
class Context {
|
||||
constructor(path, state) {
|
||||
if (path[0] === '/' && path.indexOf(base) !== 0) {
|
||||
path = base + path;
|
||||
}
|
||||
|
||||
this.canonicalPath = path;
|
||||
this.path = path.replace(base, '') || '/';
|
||||
|
||||
this.title = document.title;
|
||||
this.state = state || {};
|
||||
this.state.path = path;
|
||||
this.params = {};
|
||||
}
|
||||
|
||||
pushState() {
|
||||
history.pushState(this.state, this.title, this.canonicalPath);
|
||||
}
|
||||
|
||||
save() {
|
||||
history.replaceState(this.state, this.title, this.canonicalPath);
|
||||
}
|
||||
};
|
||||
|
||||
class Route {
|
||||
constructor(path, options) {
|
||||
options = options || {};
|
||||
this.path = (path === '*') ? '(.*)' : path;
|
||||
this.method = 'GET';
|
||||
this.regexp = pathToRegexp(this.path, this.keys = [], options);
|
||||
}
|
||||
|
||||
middleware(fn) {
|
||||
return (ctx, next) => {
|
||||
if (this.match(ctx.path, ctx.params)) {
|
||||
return fn(ctx, next);
|
||||
}
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
match(path, params) {
|
||||
const keys = this.keys;
|
||||
const qsIndex = path.indexOf('?');
|
||||
const pathname = ~qsIndex ? path.slice(0, qsIndex) : path;
|
||||
const m = this.regexp.exec(decodeURIComponent(pathname));
|
||||
|
||||
if (!m) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 1, len = m.length; i < len; ++i) {
|
||||
const key = keys[i - 1];
|
||||
const val = _decodeURLEncodedURIComponent(m[i]);
|
||||
if (val !== undefined || !(hasOwnProperty.call(params, key.name))) {
|
||||
params[key.name] = val;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class Router {
|
||||
constructor() {
|
||||
this._callbacks = [];
|
||||
this._exits = [];
|
||||
this._current = '';
|
||||
}
|
||||
|
||||
enter(path) {
|
||||
const route = new Route(path);
|
||||
for (let i = 1; i < arguments.length; ++i) {
|
||||
this._callbacks.push(route.middleware(arguments[i]));
|
||||
}
|
||||
}
|
||||
|
||||
exit(path, fn) {
|
||||
const route = new Route(path);
|
||||
for (let i = 1; i < arguments.length; ++i) {
|
||||
this._exits.push(route.middleware(arguments[i]));
|
||||
}
|
||||
}
|
||||
|
||||
start() {
|
||||
if (this._running) {
|
||||
return;
|
||||
}
|
||||
this._running = true;
|
||||
this._onPopState = _onPopState(this);
|
||||
this._onClick = _onClick(this);
|
||||
window.addEventListener('popstate', this._onPopState, false);
|
||||
document.addEventListener(clickEvent, this._onClick, false);
|
||||
const url = location.pathname + location.search + location.hash;
|
||||
this.replace(url, null, true);
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (!this._running) {
|
||||
return;
|
||||
}
|
||||
this._current = '';
|
||||
this._running = false;
|
||||
document.removeEventListener(clickEvent, this._onClick, false);
|
||||
window.removeEventListener('popstate', this._onPopState, false);
|
||||
}
|
||||
|
||||
show(path, state, push) {
|
||||
const ctx = new Context(path, state);
|
||||
this._current = ctx.path;
|
||||
this.dispatch(ctx);
|
||||
if (ctx.handled !== false && push !== false) {
|
||||
ctx.pushState();
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
replace(path, state, dispatch) {
|
||||
var ctx = new Context(path, state);
|
||||
this._current = ctx.path;
|
||||
ctx.save();
|
||||
if (dispatch) {
|
||||
this.dispatch(ctx);
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
dispatch(ctx) {
|
||||
const prev = prevContext;
|
||||
let i = 0;
|
||||
let j = 0;
|
||||
|
||||
prevContext = ctx;
|
||||
|
||||
const nextExit = () => {
|
||||
const fn = this._exits[j++];
|
||||
if (!fn) {
|
||||
return nextEnter();
|
||||
}
|
||||
fn(prev, nextExit);
|
||||
};
|
||||
|
||||
const nextEnter = () => {
|
||||
const fn = this._callbacks[i++];
|
||||
if (ctx.path !== this._current) {
|
||||
ctx.handled = false;
|
||||
return;
|
||||
}
|
||||
if (!fn) {
|
||||
return this._unhandled(ctx);
|
||||
}
|
||||
fn(ctx, nextEnter);
|
||||
};
|
||||
|
||||
if (prev) {
|
||||
nextExit();
|
||||
} else {
|
||||
nextEnter();
|
||||
}
|
||||
}
|
||||
|
||||
_unhandled(ctx) {
|
||||
if (ctx.handled) {
|
||||
return;
|
||||
}
|
||||
let current = location.pathname + location.search;
|
||||
if (current === ctx.canonicalPath) {
|
||||
return;
|
||||
}
|
||||
router.stop();
|
||||
ctx.handled = false;
|
||||
location.href = ctx.canonicalPath;
|
||||
}
|
||||
};
|
||||
|
||||
const _onPopState = router => {
|
||||
let loaded = false;
|
||||
if (document.readyState === 'complete') {
|
||||
loaded = true;
|
||||
} else {
|
||||
window.addEventListener(
|
||||
'load',
|
||||
() => {
|
||||
setTimeout(() => {
|
||||
loaded = true;
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
return e => {
|
||||
if (!loaded) {
|
||||
return;
|
||||
}
|
||||
if (e.state) {
|
||||
const path = e.state.path;
|
||||
router.replace(path, e.state, true);
|
||||
} else {
|
||||
router.show(
|
||||
location.pathname + location.hash,
|
||||
undefined,
|
||||
false);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const _onClick = router => {
|
||||
return e => {
|
||||
if (1 !== _which(e)) {
|
||||
return;
|
||||
}
|
||||
if (e.metaKey || e.ctrlKey || e.shiftKey) {
|
||||
return;
|
||||
}
|
||||
if (e.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
let el = e.path ? e.path[0] : e.target;
|
||||
while (el && el.nodeName !== 'A') {
|
||||
el = el.parentNode;
|
||||
}
|
||||
if (!el || el.nodeName !== 'A') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (el.hasAttribute('download') ||
|
||||
el.getAttribute('rel') === 'external') {
|
||||
return;
|
||||
}
|
||||
|
||||
const link = el.getAttribute('href');
|
||||
if (el.pathname === location.pathname && (el.hash || '#' === link)) {
|
||||
return;
|
||||
}
|
||||
if (link && link.indexOf('mailto:') > -1) {
|
||||
return;
|
||||
}
|
||||
if (el.target) {
|
||||
return;
|
||||
}
|
||||
if (!_isSameOrigin(el.href)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let path = el.pathname + el.search + (el.hash || '');
|
||||
|
||||
const orig = path;
|
||||
if (path.indexOf(base) === 0) {
|
||||
path = path.substr(base.length);
|
||||
}
|
||||
if (base && orig === path) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
router.show(orig);
|
||||
};
|
||||
};
|
||||
|
||||
function _which(e) {
|
||||
e = e || window.event;
|
||||
return e.which === null ? e.button : e.which;
|
||||
}
|
||||
|
||||
Router.prototype.Context = Context;
|
||||
Router.prototype.Route = Route;
|
||||
module.exports = new Router();
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const page = require('page');
|
||||
const router = require('../router.js');
|
||||
const events = require('../events.js');
|
||||
const views = require('../util/views.js');
|
||||
|
||||
|
@ -55,10 +55,9 @@ class EndlessPageView {
|
|||
}
|
||||
let topPageNumber = parseInt(topPageNode.getAttribute('data-page'));
|
||||
if (topPageNumber !== this.currentPage) {
|
||||
page.replace(
|
||||
router.replace(
|
||||
_formatUrl(ctx.clientUrl, topPageNumber),
|
||||
null,
|
||||
false,
|
||||
{},
|
||||
false);
|
||||
this.currentPage = topPageNumber;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const page = require('page');
|
||||
const router = require('../router.js');
|
||||
const config = require('../config.js');
|
||||
const misc = require('../util/misc.js');
|
||||
const views = require('../util/views.js');
|
||||
|
@ -32,7 +32,7 @@ class HomeView {
|
|||
form.querySelector('input[name=all-posts')
|
||||
.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
page('/posts/');
|
||||
router.show('/posts/');
|
||||
});
|
||||
|
||||
const searchTextInput = form.querySelector(
|
||||
|
@ -42,7 +42,7 @@ class HomeView {
|
|||
e.preventDefault();
|
||||
const text = searchTextInput.value;
|
||||
searchTextInput.blur();
|
||||
page('/posts/' + misc.formatSearchQuery({text: text}));
|
||||
router.show('/posts/' + misc.formatSearchQuery({text: text}));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const page = require('page');
|
||||
const router = require('../router.js');
|
||||
const events = require('../events.js');
|
||||
const keyboard = require('../util/keyboard.js');
|
||||
const misc = require('../util/misc.js');
|
||||
|
@ -85,12 +85,12 @@ class ManualPageView {
|
|||
|
||||
keyboard.bind(['a', 'left'], () => {
|
||||
if (currentPage > 1) {
|
||||
page.show(_formatUrl(ctx.clientUrl, currentPage - 1));
|
||||
router.show(_formatUrl(ctx.clientUrl, currentPage - 1));
|
||||
}
|
||||
});
|
||||
keyboard.bind(['d', 'right'], () => {
|
||||
if (currentPage < totalPages) {
|
||||
page.show(_formatUrl(ctx.clientUrl, currentPage + 1));
|
||||
router.show(_formatUrl(ctx.clientUrl, currentPage + 1));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
const api = require('../api.js');
|
||||
const router = require('../router.js');
|
||||
const views = require('../util/views.js');
|
||||
const keyboard = require('../util/keyboard.js');
|
||||
const page = require('page');
|
||||
const PostContentControl = require('../controls/post_content_control.js');
|
||||
const PostNotesOverlayControl
|
||||
= require('../controls/post_notes_overlay_control.js');
|
||||
|
@ -60,19 +60,19 @@ class PostView {
|
|||
|
||||
keyboard.bind('e', () => {
|
||||
if (ctx.editMode) {
|
||||
page.show('/post/' + ctx.post.id);
|
||||
router.show('/post/' + ctx.post.id);
|
||||
} else {
|
||||
page.show('/post/' + ctx.post.id + '/edit');
|
||||
router.show('/post/' + ctx.post.id + '/edit');
|
||||
}
|
||||
});
|
||||
keyboard.bind(['a', 'left'], () => {
|
||||
if (ctx.nextPostId) {
|
||||
page.show('/post/' + ctx.nextPostId);
|
||||
router.show('/post/' + ctx.nextPostId);
|
||||
}
|
||||
});
|
||||
keyboard.bind(['d', 'right'], () => {
|
||||
if (ctx.prevPostId) {
|
||||
page.show('/post/' + ctx.prevPostId);
|
||||
router.show('/post/' + ctx.prevPostId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const page = require('page');
|
||||
const router = require('../router.js');
|
||||
const settings = require('../settings.js');
|
||||
const keyboard = require('../util/keyboard.js');
|
||||
const misc = require('../util/misc.js');
|
||||
|
@ -56,14 +56,14 @@ class PostsHeaderView {
|
|||
browsingSettings.listPosts[safety]
|
||||
= !browsingSettings.listPosts[safety];
|
||||
settings.saveSettings(browsingSettings, true);
|
||||
page(url.replace(/{page}/, 1));
|
||||
router.show(url.replace(/{page}/, 1));
|
||||
}
|
||||
|
||||
_evtFormSubmit(e, searchTextInput) {
|
||||
e.preventDefault();
|
||||
const text = searchTextInput.value;
|
||||
searchTextInput.blur();
|
||||
page('/posts/' + misc.formatSearchQuery({text: text}));
|
||||
router.show('/posts/' + misc.formatSearchQuery({text: text}));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const page = require('page');
|
||||
const router = require('../router.js');
|
||||
const keyboard = require('../util/keyboard.js');
|
||||
const misc = require('../util/misc.js');
|
||||
const views = require('../util/views.js');
|
||||
|
@ -31,7 +31,7 @@ class TagsHeaderView {
|
|||
e.preventDefault();
|
||||
const text = searchTextInput.value;
|
||||
searchTextInput.blur();
|
||||
page('/tags/' + misc.formatSearchQuery({text: text}));
|
||||
router.show('/tags/' + misc.formatSearchQuery({text: text}));
|
||||
});
|
||||
|
||||
views.showView(target, source);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const page = require('page');
|
||||
const router = require('../router.js');
|
||||
const keyboard = require('../util/keyboard.js');
|
||||
const misc = require('../util/misc.js');
|
||||
const views = require('../util/views.js');
|
||||
|
@ -25,7 +25,7 @@ class UsersHeaderView {
|
|||
const searchTextInput = form.querySelector('[name=search-text]');
|
||||
const text = searchTextInput.value;
|
||||
searchTextInput.blur();
|
||||
page('/users/' + misc.formatSearchQuery({text: text}));
|
||||
router.show('/users/' + misc.formatSearchQuery({text: text}));
|
||||
});
|
||||
|
||||
views.showView(target, source);
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
"merge": "^1.2.0",
|
||||
"mousetrap": "^1.5.3",
|
||||
"nprogress": "^0.2.0",
|
||||
"page": "^1.7.1",
|
||||
"path-to-regexp": "^1.5.1",
|
||||
"stylus": "^0.54.2",
|
||||
"superagent": "^1.8.3",
|
||||
"uglify-js": "git://github.com/mishoo/UglifyJS2.git#harmony",
|
||||
|
|
Loading…
Reference in a new issue