generator-closure-stack
Version:
Yeoman generator for stack with closure compiler, soy templates, less + express
167 lines (145 loc) • 4.39 kB
JavaScript
goog.provide('$_jsNamespace_$.core.Router');
/**
* @constructor
*/
$_jsNamespace_$.core.Router = function() {
/**
* @private
* @type {Array.<{routeMatcher: function($_jsNamespace_$.core.Router.Route):boolean, page: $_jsNamespace_$.core.Router.PageConstructor}>}
*/
this.pages_ = [];
/**
* @private
* @type {jQuery|null}
*/
this.$page_ = null;
/**
* @private
* @type {$_jsNamespace_$.core.Page|null}
*/
this.currentPage_ = null;
};
$_jsNamespace_$.core.Router.prototype = {
/**
* @param {jQuery} $page
* @return {undefined}
*/
start: function($page) {
this.$page_ = $page;
this.loadPageFromUrl_();
$(window).on('popstate', this.loadPageFromUrl_.bind(this));
$('body').on('click', 'a[href]', function(e) {
var $el = $(e.currentTarget);
var href = /** @type {string} */($el.prop('href'));
if (href.indexOf(document.location.origin) === 0) {
href = href.replace(document.location.origin, '');
}
if (this.isInternalPath_(href) && !e.metaKey) {
e.preventDefault();
this.loadUrl(href);
window.history.pushState({}, '', href);
}
}.bind(this));
},
/**
* @param {function($_jsNamespace_$.core.Router.Route): boolean} routeMatcher
* @param {$_jsNamespace_$.core.Router.PageConstructor} page
* @return {undefined}
*/
register: function(routeMatcher, page) {
this.pages_.push({routeMatcher: routeMatcher, page: page});
},
/**
* @param {string} url
* @return {undefined}
*/
loadUrl: function(url) {
var route = this.extractRoute_(url);
var Page = _.find(this.pages_, function(page) { return page.routeMatcher(route); });
if (Page) {
this.currentPage_ = new Page.page({ el: this.$page_, route: route });
this.currentPage_.render();
} else {
throw new Error('Could not find page that matches route', route);
}
},
/**
* @private
* @param {string} href
* @return {boolean}
*/
isInternalPath_: function(href) {
return href.indexOf('/') === 0;
},
/**
* @private
* @return {undefined}
*/
loadPageFromUrl_: function() {
var url = window.location.pathname + window.location.search;
this.loadUrl(url);
},
/**
* @private
* @param {string} url
* @return {$_jsNamespace_$.core.Router.Route}
*/
extractRoute_: function(url) {
var components = url.split('?');
var rawPath = components[0];
var path = rawPath.split('/');
var params = {};
if (components[1]) {
var splitParams = components[1].split('&');
_.each(splitParams, function (paramPair) {
var splitPair = paramPair.split('=');
params[splitPair[0]] = (splitPair[1] || '');
});
}
return {
rawPath: rawPath,
path: path,
params: params
}
}
};
/* ----------------------------------------------------------------------------------------------- Static methods --- */
/**
* @private
* @type {$_jsNamespace_$.core.Router}
*/
$_jsNamespace_$.core.Router.instance_ = null;
/**
* @return {$_jsNamespace_$.core.Router}
*/
$_jsNamespace_$.core.Router.getInstance = function() {
if (! $_jsNamespace_$.core.Router.instance_) {
$_jsNamespace_$.core.Router.instance_ = new $_jsNamespace_$.core.Router();
}
return $_jsNamespace_$.core.Router.instance_;
};
/**
* @param {jQuery} $page
* @return {undefined}
*/
$_jsNamespace_$.core.Router.init = function($page) {
$_jsNamespace_$.core.Router.getInstance().start($page);
};
/* ----------------------------------------------------------------------------------------- Types and Interfaces --- */
/**
* @typedef {{
* rawPath: string,
* path: Array.<string>,
* params: Object.<string, string>
* }}
*/
$_jsNamespace_$.core.Router.Route;
/**
* @typedef {function(new:$_jsNamespace_$.core.Page, {el: jQuery, route: $_jsNamespace_$.core.Router.Route})}
*/
$_jsNamespace_$.core.Router.PageConstructor;
/* -------------------------------------------------------------------------------------- Advanced compile export --- */
window['$_jsNamespace_$'] = window['$_jsNamespace_$'] || {};
window['$_jsNamespace_$']['core'] = window['$_jsNamespace_$']['core'] || {};
window['$_jsNamespace_$']['core']['Router'] = $_jsNamespace_$.core.Router;
window['$_jsNamespace_$']['core']['Router']['init'] = $_jsNamespace_$.core.Router.init;