Removed PathJS dependency; refactored routing

This commit is contained in:
Marcin Kurczewski 2014-10-09 21:41:46 +02:00
parent 60ab927558
commit 6afd60feba
30 changed files with 293 additions and 262 deletions

View file

@ -2,7 +2,6 @@
"name": "szuru2",
"version": "0.0.0",
"dependencies": {
"pathjs": "~0.8.1",
"jquery.cookie": "1.4.1",
"jquery": "~2.1.1",
"underscore": "1.7.0",

View file

@ -12,7 +12,6 @@
<script type="text/javascript" src="/lib/jquery.min.js"></script>
<script type="text/javascript" src="/lib/jquery.cookie.js"></script>
<script type="text/javascript" src="/lib/path.js"></script>
<script type="text/javascript" src="/lib/underscore.min.js"></script>
<script type="text/javascript" src="/lib/mousetrap.min.js"></script>
<script type="text/javascript" src="/lib/marked.js"></script>

View file

@ -1,6 +1,6 @@
var App = App || {};
App.Bootstrap = function(auth, router, util, promise, presenterManager) {
App.Bootstrap = function(auth, router, promise, presenterManager) {
promise.wait(
auth.tryLoginFromCookie(),
@ -25,7 +25,7 @@ App.Bootstrap = function(auth, router, util, promise, presenterManager) {
};
App.DI.registerSingleton('bootstrap', ['auth', 'router', 'util', 'promise', 'presenterManager'], App.Bootstrap);
App.DI.registerSingleton('bootstrap', ['auth', 'router', 'promise', 'presenterManager'], App.Bootstrap);
App.DI.registerManual('jQuery', function() { return window.$; });
App.DI.registerManual('pathJs', function() { return window.pathjs; });
App.DI.registerManual('_', function() { return window._; });

View file

@ -51,7 +51,8 @@ App.Pager = function(
function setSearchParams(newSearchParams) {
setPage(1);
searchParams = newSearchParams;
searchParams = _.extend({}, newSearchParams);
delete searchParams.page;
}
function retrieve() {
@ -64,7 +65,8 @@ App.Pager = function(
resolve({
entities: response.json.data,
totalRecords: totalRecords});
totalRecords: totalRecords,
totalPages: totalPages});
}).fail(function(response) {
reject(response);

View file

@ -12,7 +12,7 @@ App.Presenters.GlobalCommentListPresenter = function(
var $el;
var templates = {};
function init(args, loaded) {
function init(params, loaded) {
$el = jQuery('#content');
topNavigationPresenter.select('comments');
@ -38,25 +38,16 @@ App.Presenters.GlobalCommentListPresenter = function(
},
},
function() {
onArgsChanged(args);
reinit(params, function() {});
});
})
.fail(function() { console.log(new Error(arguments)); });
}
function reinit(args, loaded) {
function reinit(params, loaded) {
pagerPresenter.reinit({query: params.query});
loaded();
onArgsChanged(args);
}
function onArgsChanged(args) {
var searchArgs = util.parseComplexRouteArgs(args.searchArgs);
pagerPresenter.reinit({
page: searchArgs.page,
searchParams: {
query: searchArgs.query,
order: searchArgs.order}});
}
function deinit() {
@ -79,6 +70,7 @@ App.Presenters.GlobalCommentListPresenter = function(
var comments = data.comments;
var $post = jQuery('<li>' + templates.listItem({
util: util,
post: post,
postTemplate: templates.post,
}) + '</li>');
@ -104,6 +96,7 @@ App.Presenters.GlobalCommentListPresenter = function(
deinit: deinit,
render: render,
};
};
App.DI.register('globalCommentListPresenter', ['_', 'jQuery', 'util', 'promise', 'pagerPresenter', 'topNavigationPresenter'], App.Presenters.GlobalCommentListPresenter);

View file

@ -7,7 +7,7 @@ App.Presenters.HelpPresenter = function(
var $el = jQuery('#content');
function init(args, loaded) {
function init(params, loaded) {
topNavigationPresenter.select('help');
topNavigationPresenter.changeTitle('Help');
render();

View file

@ -15,7 +15,7 @@ App.Presenters.HomePresenter = function(
var globals;
var post;
function init(args, loaded) {
function init(params, loaded) {
topNavigationPresenter.select('home');
topNavigationPresenter.changeTitle('Home');

View file

@ -13,12 +13,12 @@ App.Presenters.LoginPresenter = function(
var $el = jQuery('#content');
var $messages;
var templates = {};
var previousRoute;
var previousLocation;
function init(args, loaded) {
function init(params, loaded) {
topNavigationPresenter.select('login');
topNavigationPresenter.changeTitle('Login');
previousRoute = args.previousRoute;
previousLocation = params.previousLocation;
promise.wait(util.promiseTemplate('login-form'))
.then(function(template) {
templates.login = template;
@ -66,8 +66,8 @@ App.Presenters.LoginPresenter = function(
}
function finishLogin() {
if (previousRoute && !previousRoute.match(/logout|password-reset|activate|register/)) {
router.navigate(previousRoute);
if (previousLocation && !previousLocation.match(/logout|password-reset|activate|register/)) {
router.navigate(previousLocation);
} else {
router.navigateToMainPage();
}

View file

@ -11,7 +11,7 @@ App.Presenters.LogoutPresenter = function(
var $messages = jQuery('#content');
function init(args, loaded) {
function init(params, loaded) {
topNavigationPresenter.select('logout');
topNavigationPresenter.changeTitle('Logout');
promise.wait(auth.logout())

View file

@ -26,17 +26,20 @@ App.Presenters.PagerPresenter = function(
var baseUri;
var updateCallback;
function init(args, loaded) {
baseUri = args.baseUri;
updateCallback = args.updateCallback;
function init(params, loaded) {
baseUri = params.baseUri;
updateCallback = params.updateCallback;
messagePresenter.instant = true;
$target = args.$target;
targetContent = jQuery(args.$target).html();
$target = params.$target;
targetContent = jQuery(params.$target).html();
pager.init({url: args.backendUri});
pager.setSearchParams(args.searchParams);
if (forceClear) {
clearContent();
}
pager.init({url: params.backendUri});
setQuery(params.query);
promise.wait(util.promiseTemplate('pager'))
.then(function(template) {
@ -46,62 +49,28 @@ App.Presenters.PagerPresenter = function(
});
}
function reinit(args, loaded) {
forceClear = !_.isEqual(args.searchParams, pager.getSearchParams()) || parseInt(args.page) !== pager.getPage();
pager.setSearchParams(args.searchParams);
pager.setPage(args.page || 1);
function reinit(params, loaded) {
if (forceClear) {
clearContent();
}
setQuery(params.query);
promise.wait(retrieve())
.then(loaded)
.fail(loaded);
if (!endlessScroll) {
keyboard.keydown('a', prevPage);
keyboard.keydown('d', nextPage);
keyboard.keydown('a', function() { pager.prevPage(); syncUrl(); });
keyboard.keydown('d', function() { pager.nextPage(); syncUrl(); });
}
}
function deinit() {
detachNextPageLoader();
}
function prevPage() {
pager.prevPage();
syncUrl();
}
function nextPage() {
pager.nextPage();
syncUrl();
}
function nextPageInplace() {
pager.nextPage();
syncUrlInplace();
}
function setPage(newPage) {
pager.setPage(newPage);
syncUrl();
}
function setSearchParams(newSearchParams) {
if (_.isEqual(pager.getSearchParams(), newSearchParams)) {
return;
}
clearContent();
pager.setSearchParams(newSearchParams);
syncUrl();
}
function getUrl() {
return util.compileComplexRouteArgs(
baseUri,
_.extend(
{page: pager.getPage()},
pager.getSearchParams()));
return util.appendComplexRouteParam(baseUri, _.extend({}, pager.getSearchParams(), {page: pager.getPage()}));
}
function syncUrl() {
@ -148,9 +117,11 @@ App.Presenters.PagerPresenter = function(
showPageList();
}
if (pager.getPage() < response.totalPages) {
attachNextPageLoader();
}
refreshPageList();
hideSpinner();
attachNextPageLoader();
resolve();
}).fail(function(response) {
clearContent();
@ -177,7 +148,8 @@ App.Presenters.PagerPresenter = function(
var baseLine = $target.offset().top + $target.innerHeight();
var scrollY = jQuery(window).scrollTop() + jQuery(window).height();
if (scrollY > baseLine) {
nextPageInplace();
pager.nextPage();
syncUrlInplace();
window.clearInterval(scrollInterval);
}
}, 100);
@ -207,7 +179,8 @@ App.Presenters.PagerPresenter = function(
var $a = jQuery('<a/>');
$a.click(function() {
setPage(page);
pager.setPage(page);
syncUrl();
});
$a.addClass('big-button');
$a.text(page);
@ -231,12 +204,32 @@ App.Presenters.PagerPresenter = function(
}
}
function setQuery(query) {
if (!query) {
return;
}
query.page = parseInt(query.page) || 1;
var page = query.page;
delete query.page;
forceClear =
query.query !== pager.getSearchParams().query ||
query.order !== pager.getSearchParams().order ||
parseInt(page) !== pager.getPage();
pager.setSearchParams(query);
pager.setPage(page);
}
function setQueryAndSyncUrl(query) {
setQuery(query);
syncUrl();
}
return {
init: init,
reinit: reinit,
deinit: deinit,
setPage: setPage,
setSearchParams: setSearchParams,
syncUrl: syncUrl,
setQuery: setQueryAndSyncUrl,
};
};

View file

@ -18,10 +18,10 @@ App.Presenters.PostCommentListPresenter = function(
var post;
var comments = [];
function init(args, loaded) {
$el = args.$target;
post = args.post;
comments = args.comments || [];
function init(params, loaded) {
$el = params.$target;
post = params.post;
comments = params.comments || [];
privileges = {
canListComments: auth.hasPrivilege(auth.privileges.listComments),
@ -49,7 +49,7 @@ App.Presenters.PostCommentListPresenter = function(
loaded();
if (comments.length === 0) {
promise.wait(api.get('/comments/' + args.post.id))
promise.wait(api.get('/comments/' + params.post.id))
.then(function(response) {
comments = response.json.data;
render();
@ -222,6 +222,7 @@ App.Presenters.PostCommentListPresenter = function(
init: init,
render: render,
};
};
App.DI.register('postCommentListPresenter', ['_', 'jQuery', 'util', 'promise', 'api', 'auth', 'topNavigationPresenter', 'messagePresenter'], App.Presenters.PostCommentListPresenter);

View file

@ -17,13 +17,13 @@ App.Presenters.PostListPresenter = function(
var $el = jQuery('#content');
var $searchInput;
var searchArgs;
var params;
function init(args, loaded) {
function init(_params, loaded) {
topNavigationPresenter.select('posts');
topNavigationPresenter.changeTitle('Posts');
searchArgs = util.parseComplexRouteArgs(args.searchArgs);
searchArgs.page = parseInt(searchArgs.page) || 1;
params = _params;
params.query = params.query || {};
promise.wait(
util.promiseTemplate('post-list'),
@ -44,23 +44,14 @@ App.Presenters.PostListPresenter = function(
},
},
function() {
onArgsChanged(args);
reinit(params, function() {});
});
});
}
function reinit(args, loaded) {
function reinit(params, loaded) {
pagerPresenter.reinit({query: params.query});
loaded();
onArgsChanged(args);
}
function onArgsChanged(args) {
searchArgs = util.parseComplexRouteArgs(args.searchArgs);
pagerPresenter.reinit({
page: searchArgs.page,
searchParams: {
query: searchArgs.query,
order: searchArgs.order}});
}
function deinit() {
@ -72,7 +63,7 @@ App.Presenters.PostListPresenter = function(
$searchInput = $el.find('input[name=query]');
App.Controls.AutoCompleteInput($searchInput);
$searchInput.val(searchArgs.query);
$searchInput.val(params.query.query);
$searchInput.keydown(searchInputKeyPressed);
$el.find('form').submit(searchFormSubmitted);
@ -94,7 +85,8 @@ App.Presenters.PostListPresenter = function(
_.each(posts, function(post) {
var $post = jQuery('<li>' + templates.listItem({
searchArgs: searchArgs,
util: util,
query: params.query,
post: post,
}) + '</li>');
util.loadImagesNicely($post.find('img'));
@ -116,9 +108,8 @@ App.Presenters.PostListPresenter = function(
function updateSearch() {
$searchInput.blur();
pagerPresenter.setSearchParams({
query: $searchInput.val().trim(),
order: searchArgs.order});
params.query.query = $searchInput.val().trim();
pagerPresenter.setQuery(params.query);
}
return {

View file

@ -20,9 +20,9 @@ App.Presenters.PostPresenter = function(
var $messages = $el;
var templates = {};
var params;
var postNameOrId;
var searchArgs;
var post;
var privileges = {};
@ -34,7 +34,7 @@ App.Presenters.PostPresenter = function(
var postContent;
var postThumbnail;
function init(args, loaded) {
function init(params, loaded) {
topNavigationPresenter.select('posts');
postsAroundCalculator.resetCache();
@ -64,18 +64,19 @@ App.Presenters.PostPresenter = function(
templates.postContent = postContentTemplate;
templates.history = historyTemplate;
reinit(args, loaded);
reinit(params, loaded);
}).fail(function(response) {
showGenericError(response);
loaded();
});
}
function reinit(args, loaded) {
postNameOrId = args.postNameOrId;
function reinit(_params, loaded) {
params = _params;
params.query = params.query || {};
params.query.page = parseInt(params.query.page) || 1;
searchArgs = util.parseComplexRouteArgs(args.searchArgs);
searchArgs.page = parseInt(searchArgs.page) || 1;
postNameOrId = params.postNameOrId;
promise.wait(refreshPost())
.then(function() {
@ -86,8 +87,7 @@ App.Presenters.PostPresenter = function(
}
function attachLinksToPostsAround() {
var searchParams = {query: searchArgs.query, order: searchArgs.order};
promise.wait(postsAroundCalculator.getLinksToPostsAround(searchParams, searchArgs.page, post.id))
promise.wait(postsAroundCalculator.getLinksToPostsAround(params.query, post.id))
.then(function(nextPostUrl, prevPostUrl) {
var $prevPost = $el.find('#post-current-search .right a');
var $nextPost = $el.find('#post-current-search .left a');
@ -175,7 +175,7 @@ App.Presenters.PostPresenter = function(
function renderPostTemplate() {
return templates.post({
searchArgs: searchArgs,
query: params.query,
post: post,
ownScore: post.ownScore,
postFavorites: post.favorites,

View file

@ -23,7 +23,7 @@ App.Presenters.PostUploadPresenter = function(
var fileDropper;
var interactionEnabled = true;
function init(args, loaded) {
function init(params, loaded) {
topNavigationPresenter.select('upload');
topNavigationPresenter.changeTitle('Upload');

View file

@ -13,7 +13,7 @@ App.Presenters.RegistrationPresenter = function(
var templates = {};
var $messages;
function init(args, loaded) {
function init(params, loaded) {
topNavigationPresenter.select('register');
topNavigationPresenter.changeTitle('Registration');
promise.wait(util.promiseTemplate('registration-form'))

View file

@ -13,7 +13,7 @@ App.Presenters.TagListPresenter = function(
var $el = jQuery('#content');
var templates = {};
function init(args, loaded) {
function init(params, loaded) {
topNavigationPresenter.select('tags');
topNavigationPresenter.changeTitle('Tags');
@ -36,28 +36,23 @@ App.Presenters.TagListPresenter = function(
},
},
function() {
reinit(args, function() {});
reinit(params, function() {});
});
});
}
function reinit(args, loaded) {
loaded();
function reinit(params, loaded) {
params.query = params.query || {};
params.query.order = params.query.order || 'name,asc';
updateActiveOrder(params.query.order);
var searchArgs = util.parseComplexRouteArgs(args.searchArgs);
searchArgs.order = searchArgs.order || 'name,asc';
searchArgs.page = parseInt(searchArgs.page) || 1;
updateActiveOrder(searchArgs.order);
pagerPresenter.reinit({
page: searchArgs.page,
searchParams: {
order: searchArgs.order}});
pagerPresenter.reinit({query: params.query});
keyboard.keyup('p', function() {
$el.find('table a').eq(0).focus();
});
loaded();
}
function deinit() {

View file

@ -15,7 +15,7 @@ App.Presenters.TagPresenter = function(
var tagName;
function init(args, loaded) {
function init(params, loaded) {
topNavigationPresenter.select('tags');
topNavigationPresenter.changeTitle('Tags');
@ -26,12 +26,12 @@ App.Presenters.TagPresenter = function(
templates.tag = tagTemplate;
templates.postListItem = postListItemTemplate;
reinit(args, loaded);
reinit(params, loaded);
});
}
function reinit(args, loaded) {
tagName = args.tagName;
function reinit(params, loaded) {
tagName = params.tagName;
render();
loaded();
@ -55,8 +55,9 @@ App.Presenters.TagPresenter = function(
var $target = $el.find('.post-list ul');
_.each(posts, function(post) {
var $post = jQuery('<li>' + templates.postListItem({
util: util,
post: post,
searchArgs: {query: tagName},
query: {query: tagName},
}) + '</li>');
$target.append($post);
});
@ -72,6 +73,7 @@ App.Presenters.TagPresenter = function(
init: init,
reinit: reinit,
};
};
App.DI.register('tagPresenter', ['_', 'jQuery', 'util', 'promise', 'api', 'keyboard', 'topNavigationPresenter'], App.Presenters.TagPresenter);

View file

@ -12,7 +12,7 @@ App.Presenters.TopNavigationPresenter = function(
var templates = {};
var baseTitle = document.title;
function init(args, loaded) {
function init(params, loaded) {
promise.wait(util.promiseTemplate('top-navigation'))
.then(function(template) {
templates.topNavigation = template;

View file

@ -15,9 +15,9 @@ App.Presenters.UserAccountRemovalPresenter = function(
var user;
var privileges = {};
function init(args, loaded) {
user = args.user;
target = args.target;
function init(params, loaded) {
user = params.user;
target = params.target;
privileges.canDeleteAccount =
auth.hasPrivilege(auth.privileges.deleteAllAccounts) ||

View file

@ -17,9 +17,9 @@ App.Presenters.UserAccountSettingsPresenter = function(
var avatarContent;
var fileDropper;
function init(args, loaded) {
user = args.user;
target = args.target;
function init(params, loaded) {
user = params.user;
target = params.target;
privileges = {
canBan:

View file

@ -17,20 +17,20 @@ App.Presenters.UserActivationPresenter = function(
var formHidden = false;
var operation;
function init(args, loaded) {
function init(params, loaded) {
topNavigationPresenter.select('login');
topNavigationPresenter.changeTitle('Account recovery');
reinit(args, loaded);
reinit(params, loaded);
}
function reinit(args, loaded) {
operation = args.operation;
function reinit(params, loaded) {
operation = params.operation;
promise.wait(util.promiseTemplate('user-query-form'))
.then(function(template) {
templates.userQuery = template;
if (args.token) {
if (params.token) {
hideForm();
confirmToken(args.token);
confirmToken(params.token);
} else {
showForm();
}

View file

@ -14,9 +14,9 @@ App.Presenters.UserBrowsingSettingsPresenter = function(
var user;
var privileges = {};
function init(args, loaded) {
user = args.user;
target = args.target;
function init(params, loaded) {
user = params.user;
target = params.target;
privileges.canChangeBrowsingSettings = auth.isLoggedIn(user.name) && user.name === auth.getCurrentUser().name;

View file

@ -12,8 +12,9 @@ App.Presenters.UserListPresenter = function(
var $el = jQuery('#content');
var templates = {};
var params;
function init(args, loaded) {
function init(params, loaded) {
topNavigationPresenter.select('users');
topNavigationPresenter.changeTitle('Users');
@ -36,23 +37,19 @@ App.Presenters.UserListPresenter = function(
},
},
function() {
reinit(args, function() {});
reinit(params, function() {});
});
});
}
function reinit(args, loaded) {
function reinit(_params, loaded) {
params = _params;
params.query = params.query || {};
params.query.order = params.query.order || 'name,asc';
updateActiveOrder(params.query.order);
pagerPresenter.reinit({query: params.query});
loaded();
var searchArgs = util.parseComplexRouteArgs(args.searchArgs);
searchArgs.order = searchArgs.order || 'name,asc';
searchArgs.page = parseInt(searchArgs.page) || 1;
updateActiveOrder(searchArgs.order);
pagerPresenter.reinit({
page: searchArgs.page,
searchParams: {
order: searchArgs.order}});
}
function deinit() {
@ -90,7 +87,8 @@ App.Presenters.UserListPresenter = function(
e.preventDefault();
var $orderLink = jQuery(this);
var activeSearchOrder = $orderLink.attr('data-order');
pagerPresenter.setSearchParams({order: activeSearchOrder});
params.query.order = activeSearchOrder;
pagerPresenter.setQuery(params.query);
}
return {

View file

@ -22,32 +22,32 @@ App.Presenters.UserPresenter = function(
var userName = null;
var activeTab;
function init(args, loaded) {
function init(params, loaded) {
promise.wait(util.promiseTemplate('user'))
.then(function(template) {
$messages = $el.find('.messages');
templates.user = template;
reinit(args, loaded);
reinit(params, loaded);
});
}
function reinit(args, loaded) {
if (args.userName !== userName) {
userName = args.userName;
function reinit(params, loaded) {
if (params.userName !== userName) {
userName = params.userName;
topNavigationPresenter.select(auth.isLoggedIn(userName) ? 'my-account' : 'users');
topNavigationPresenter.changeTitle(userName);
promise.wait(api.get('/users/' + userName))
.then(function(response) {
user = response.json;
var extendedContext = _.extend(args, {user: user});
var extendedContext = _.extend(params, {user: user});
presenterManager.initPresenters([
[userBrowsingSettingsPresenter, _.extend({}, extendedContext, {target: '#browsing-settings-target'})],
[userAccountSettingsPresenter, _.extend({}, extendedContext, {target: '#account-settings-target'})],
[userAccountRemovalPresenter, _.extend({}, extendedContext, {target: '#account-removal-target'})]],
function() {
initTabs(args);
initTabs(params);
loaded();
});
@ -58,13 +58,13 @@ App.Presenters.UserPresenter = function(
});
} else {
initTabs(args);
initTabs(params);
loaded();
}
}
function initTabs(args) {
activeTab = args.tab || 'basic-info';
function initTabs(params) {
activeTab = params.tab || 'basic-info';
render();
}

View file

@ -1,34 +1,16 @@
var App = App || {};
App.Router = function(pathJs, _, jQuery, promise, util, appState, presenterManager) {
App.Router = function(_, jQuery, promise, util, appState, presenterManager) {
var root = '#/';
var previousLocation = window.location.href;
var routes = [];
injectRoutes();
function navigateToMainPage() {
window.location.href = root;
}
function navigate(url) {
window.location.href = url;
}
function navigateInplace(url) {
if ('replaceState' in history) {
history.replaceState('', '', url);
pathJs.dispatch(document.location.hash);
} else {
navigate(url);
}
}
function start() {
pathJs.listen();
}
function injectRoutes() {
inject('', 'homePresenter');
inject('#/', 'homePresenter');
inject('#/home', 'homePresenter');
inject('#/login', 'loginPresenter');
inject('#/logout', 'logoutPresenter');
@ -36,24 +18,42 @@ App.Router = function(pathJs, _, jQuery, promise, util, appState, presenterManag
inject('#/upload', 'postUploadPresenter');
inject('#/password-reset(/:token)', 'userActivationPresenter', {operation: 'passwordReset'});
inject('#/activate(/:token)', 'userActivationPresenter', {operation: 'activation'});
inject('#/users(/:searchArgs)', 'userListPresenter');
inject('#/users(/:!query)', 'userListPresenter');
inject('#/user/:userName(/:tab)', 'userPresenter');
inject('#/posts(/:searchArgs)', 'postListPresenter');
inject('#/post(/:postNameOrId)(/:searchArgs)', 'postPresenter');
inject('#/comments(/:searchArgs)', 'globalCommentListPresenter');
inject('#/tags(/:searchArgs)', 'tagListPresenter');
inject('#/tag(/:tagName)', 'tagPresenter');
inject('#/posts(/:!query)', 'postListPresenter');
inject('#/post/:postNameOrId(/:!query)', 'postPresenter');
inject('#/comments(/:!query)', 'globalCommentListPresenter');
inject('#/tags(/:!query)', 'tagListPresenter');
inject('#/tag/:tagName', 'tagPresenter');
inject('#/help', 'helpPresenter');
setRoot('#/home');
}
function setRoot(newRoot) {
root = newRoot;
pathJs.root(newRoot);
function navigate(url) {
window.location.href = url;
}
function inject(path, presenterName, additionalParams) {
pathJs.map(path).to(function() {
function navigateToMainPage() {
navigate(root);
}
function navigateInplace(url) {
if ('replaceState' in history) {
history.replaceState('', '', url);
dispatch();
} else {
navigate(url);
}
}
function start() {
window.onpopstate = function() {
dispatch();
};
dispatch();
}
function inject(definition, presenterName, additionalParams) {
routes.push(new Route(definition, function(params) {
if (util.isExitConfirmationEnabled()) {
if (window.location.href === previousLocation) {
return;
@ -67,28 +67,106 @@ App.Router = function(pathJs, _, jQuery, promise, util, appState, presenterManag
}
}
params = _.extend({}, params, additionalParams, {previousLocation: previousLocation});
//abort every operation that can be executed
promise.abortAll();
previousLocation = window.location.href;
var finalParams = _.extend(
this.params,
additionalParams,
{previousRoute: pathJs.routes.previous});
var presenter = App.DI.get(presenterName);
presenter.name = presenterName;
presenterManager.switchContentPresenter(presenter, finalParams);
});
presenterManager.switchContentPresenter(presenter, params);
}));
}
function dispatch() {
var url = document.location.hash;
for (var i = 0; i < routes.length; i ++) {
var route = routes[i];
if (route.match(url)) {
route.callback(route.params);
return true;
}
}
//todo: 404
console.log(new Error('Unhandled route: ' + url));
return false;
}
function parseComplexParamValue(value) {
var result = {};
var params = (value || '').split(/;/);
for (var i = 0; i < params.length; i ++) {
var param = params[i];
if (!param) {
continue;
}
var kv = param.split(/=/);
result[kv[0]] = kv[1];
}
return result;
}
function Route(definition, callback) {
var possibleRoutes = getPossibleRoutes(definition);
function getPossibleRoutes(routeDefinition) {
var parts = [];
var re = new RegExp('\\(([^}]+?)\\)', 'g');
while (true) {
var text = re.exec(routeDefinition);
if (!text) {
break;
}
parts.push(text[1]);
}
var possibleRoutes = [routeDefinition.split('(')[0]];
for (var i = 0; i < parts.length; i ++) {
possibleRoutes.push(possibleRoutes[possibleRoutes.length - 1] + parts[i]);
}
return possibleRoutes;
}
function match(url) {
var params = {};
for (var i = 0; i < possibleRoutes.length; i ++) {
var possibleRoute = possibleRoutes[i];
var compare = url;
var possibleRouteParts = possibleRoute.split('/');
var compareParts = compare.split('/');
if (possibleRoute.search(':') > 0) {
for (var j = 0; j < possibleRouteParts.length; j ++) {
if ((j < compareParts.length) && (possibleRouteParts[j].charAt(0) === ':')) {
var key = possibleRouteParts[j].substring(1);
var value = compareParts[j];
if (key.charAt(0) === '!') {
key = key.substring(1);
value = parseComplexParamValue(value);
}
params[key] = value;
compare = compare.replace(compareParts[j], possibleRouteParts[j]);
}
}
}
if (possibleRoute === compare) {
this.params = params;
return true;
}
}
return false;
}
this.match = match;
this.callback = callback;
}
return {
start: start,
navigate: navigate,
navigateInplace: navigateInplace,
navigateToMainPage: navigateToMainPage,
};
};
App.DI.registerSingleton('router', ['pathJs', '_', 'jQuery', 'promise', 'util', 'appState', 'presenterManager'], App.Router);
App.DI.registerSingleton('router', ['_', 'jQuery', 'promise', 'util', 'appState', 'presenterManager'], App.Router);

View file

@ -9,10 +9,10 @@ App.Services.PostsAroundCalculator = function(_, promise, util, pager) {
pager.resetCache();
}
function getLinksToPostsAround(searchParams, page, postId) {
function getLinksToPostsAround(query, postId) {
return promise.make(function(resolve, reject) {
pager.setSearchParams(searchParams);
pager.setPage(page);
pager.setSearchParams(query);
pager.setPage(query.page);
promise.wait(pager.retrieveCached())
.then(function(response) {
var postIds = _.pluck(response.entities, 'id');
@ -23,8 +23,8 @@ App.Services.PostsAroundCalculator = function(_, promise, util, pager) {
}
promise.wait(
getLinkToPostAround(postIds, position, page, -1),
getLinkToPostAround(postIds, position, page, 1))
getLinkToPostAround(postIds, position, query.page, -1),
getLinkToPostAround(postIds, position, query.page, 1))
.then(function(nextPostUrl, prevPostUrl) {
resolve(nextPostUrl, prevPostUrl);
});
@ -35,7 +35,7 @@ App.Services.PostsAroundCalculator = function(_, promise, util, pager) {
function getLinkToPostAround(postIds, position, page, direction) {
return promise.make(function(resolve, reject) {
if (position + direction >= 0 && position + direction < postIds.length) {
var url = util.compileComplexRouteArgs(
var url = util.appendComplexRouteParam(
'#/post/' + postIds[position + direction],
_.extend({page: page}, pager.getSearchParams()));
resolve(url);
@ -47,7 +47,7 @@ App.Services.PostsAroundCalculator = function(_, promise, util, pager) {
_.last(response.entities) :
_.first(response.entities);
var url = util.compileComplexRouteArgs(
var url = util.appendComplexRouteParam(
'#/post/' + post.id,
_.extend({page: page + direction}, pager.getSearchParams()));
resolve(url);

View file

@ -31,31 +31,6 @@ App.Util = function(_, jQuery, marked, promise) {
});
}
function parseComplexRouteArgs(args) {
var result = {};
args = (args || '').split(/;/);
for (var i = 0; i < args.length; i ++) {
var arg = args[i];
if (!arg) {
continue;
}
var kv = arg.split(/=/);
result[kv[0]] = kv[1];
}
return result;
}
function compileComplexRouteArgs(baseUri, args) {
var result = baseUri + '/';
_.each(args, function(v, k) {
if (typeof(v) !== 'undefined') {
result += k + '=' + v + ';';
}
});
result = result.slice(0, -1);
return result;
}
function promiseTemplate(templateName) {
return promiseTemplateFromDOM(templateName) ||
promiseTemplateWithAJAX(templateName);
@ -218,10 +193,18 @@ App.Util = function(_, jQuery, marked, promise) {
return postDecorator(marked(preDecorator(text), options));
}
function appendComplexRouteParam(baseUri, params) {
var result = baseUri + '/';
_.each(params, function(v, k) {
if (typeof(v) !== 'undefined') {
result += k + '=' + v + ';';
}
});
return result.slice(0, -1);
}
return {
promiseTemplate: promiseTemplate,
parseComplexRouteArgs: parseComplexRouteArgs,
compileComplexRouteArgs: compileComplexRouteArgs,
formatRelativeTime: formatRelativeTime,
formatFileSize: formatFileSize,
formatMarkdown: formatMarkdown,
@ -230,6 +213,7 @@ App.Util = function(_, jQuery, marked, promise) {
isExitConfirmationEnabled: isExitConfirmationEnabled,
transparentPixel: transparentPixel,
loadImagesNicely: loadImagesNicely,
appendComplexRouteParam: appendComplexRouteParam,
};
};

View file

@ -1,6 +1,6 @@
<div class="post-comment">
<div class="post">
<%= postTemplate({post: post}) %>
<%= postTemplate({post: post, util: util}) %>
</div>
<div class="post-comments-target">

View file

@ -1,10 +1,6 @@
<div class="post-small post-type-<%= post.contentType %> ">
<a class="link"
<% if (typeof(searchArgs) !== 'undefined') { %>
href="#/post/<%= post.id %>/query=<%= searchArgs.query %>;order=<%= searchArgs.order %>;page=<%= searchArgs.page %>"
<% } else { %>
href="#/post/<%= post.id %>"
<% } %>
href="<%= util.appendComplexRouteParam('#/post/' + post.id, typeof(query) !== 'undefined' ? query : {}) %>"
title="<%= _.map(post.tags, function(tag) { return '#' + tag.name; }).join(', ') %>">
<img width="160" height="160" class="thumb" src="/data/thumbnails/160x160/posts/<%= post.name %>" alt="<%= post.idMarkdown %>"/>

View file

@ -10,8 +10,8 @@
</div>
<div class="search">
<a href="#/posts/query=<%= searchArgs.query %>;order=<%= searchArgs.order %>">
Current search: <%= searchArgs.query || '-' %>
<a href="#/posts/query=<%= query.query %>;order=<%= query.order %>">
Current search: <%= query.query || '-' %>
</a>
</div>