este-library-oldschool
Version:
Library for github.com/steida/este.git
328 lines (297 loc) • 8.4 kB
JavaScript
// Generated by github.com/steida/coffee2closure 900.1.18
/**
@fileoverview hashchange/pushState router. It watches elements with href
attribute and prevents default redirecting behaviour. But you can still use
right click and other ways to open link in new tab.
Anchor with hashchange navigation. Slash is required to prevent scroll jumps
when element with the same id as anchor href is matched.
```html
<a href='#/about'>about</a>
```
Anchor with pushState navigation.
```html
<a href='/about'>about</a>
```
Smart back button. It calls history.back() if there is any, otherwise it will
work as normal link. Typical scenario: Someone opens ajax link in new tab,
then back button redirects him to href, home or listing for example. If he
opens link in the same tab, then smart back button redirects him to previous
page.
```html
<a e-router-back-button href='#/home'>back</a>
```
Ignored hrefs.
```html
<a href='http(s)://...'>foo</a>
<a href='//...'>foo</a>
<a e-router-ignore href='/foo'>foo</a>
```
Touch devices support.
For touch devices support remember to render links with 'touch-action'
attribute. It enables Google Polymer PointerEvents.
@see http://www.polymer-project.org/platform/pointer-events.html
@see /demos/router/routerhash.html
@see /demos/router/routerhashtouch.html
@see /demos/router/routerhtml5.html
*/
var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.superClass_ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
goog.provide('este.Router');
goog.require('este.array');
goog.require('este.Base');
goog.require('este.dom');
goog.require('este.mobile');
goog.require('este.router.Route');
goog.require('este.string');
/**
@param {este.History} history
@param {este.events.GestureHandler} gestureHandler
@constructor
@extends {este.Base}
*/
este.Router = function(history, gestureHandler) {
this.history = history;
this.gestureHandler = gestureHandler;
este.Router.superClass_.constructor.call(this);
this.routes = [];
}
extend(este.Router, superClass);
/**
By default router will change url immediately. With this option we can
disable this behaviour. It's useful for async routing in este.App.
@type {boolean}
*/
este.Router.prototype.navigateImmediately = true;
/**
@type {este.History}
@protected
*/
este.Router.prototype.history = null;
/**
@type {este.events.GestureHandler}
@protected
*/
este.Router.prototype.gestureHandler = null;
/**
@type {Array.<este.router.Route>}
@protected
*/
este.Router.prototype.routes = null;
/**
@type {boolean}
@protected
*/
este.Router.prototype.ignoreNextOnHistoryNavigate = false;
/**
@param {string} path
@param {Function} show
@param {este.router.Route.Options=} options
@return {este.Router}
*/
este.Router.prototype.add = function(path, show, options) {
var route;
if (options == null) {
options = {};
}
path = este.string.stripSlashHashPrefixes(path);
route = new este.router.Route(path, show, options);
this.routes.push(route);
return this;
};
/**
@param {string} path
@return {boolean}
*/
este.Router.prototype.remove = function(path) {
path = este.string.stripSlashHashPrefixes(path);
return este.array.removeAllIf(this.routes, function(item) {
return item.path === path;
});
};
/**
@return {boolean}
*/
este.Router.prototype.isHtml5historyEnabled = function() {
return this.history.html5historyEnabled;
};
/**
Start router.
*/
este.Router.prototype.start = function() {
this.on(this.gestureHandler.getElement(), 'click', this.onGestureHandlerElementClick);
this.on(this.gestureHandler, 'tap', this.onGestureHandlerTap);
this.on(this.history, 'navigate', this.onHistoryNavigate);
this.history.setEnabled(true);
};
/**
@param {string} token
*/
este.Router.prototype.navigate = function(token) {
token = este.string.stripSlashHashPrefixes(token);
return this.history.setToken(token);
};
/**
@param {string} path
@param {Object=} params
@param {boolean=} silent
*/
este.Router.prototype.pathNavigate = function(path, params, silent) {
var route;
if (silent == null) {
silent = false;
}
path = este.string.stripSlashHashPrefixes(path);
route = this.findRoute(path);
if (!route) {
return;
}
this.ignoreNextOnHistoryNavigate = silent;
return this.navigate(route.createUrl(params));
};
/**
@param {string} path
@protected
*/
este.Router.prototype.findRoute = function(path) {
path = este.string.stripSlashHashPrefixes(path);
return goog.array.find(this.routes, function(item) {
return item.path === path;
});
};
/**
Suppress default anchor redirecting behaviour. If no touch-action attribute
is defined on target or its parents, then we need to delegate click to
onGestureHandlerTap handler.
https://github.com/Polymer/PointerEvents#basic-usage
@param {goog.events.BrowserEvent} e
@protected
*/
este.Router.prototype.onGestureHandlerElementClick = function(e) {
var token;
if (!este.dom.isRoutingClick(e)) {
return;
}
token = this.tryGetToken(e);
if (!token) {
return;
}
e.preventDefault();
if (!this.gestureHandler.targetIsTouchActionEnabled(e.target)) {
return this.onGestureHandlerTap(e, token);
}
};
/**
@param {goog.events.BrowserEvent} e
@param {string=} token
@protected
*/
este.Router.prototype.onGestureHandlerTap = function(e, token) {
if (this.isBackButton(e)) {
este.mobile.back();
return;
}
if (token == null) {
token = this.tryGetToken(e);
}
if (!token) {
return;
}
if (this.navigateImmediately) {
this.navigate(token);
return;
}
return this.processRoutes(token, false);
};
/**
@param {goog.history.Event} e
@protected
*/
este.Router.prototype.onHistoryNavigate = function(e) {
var token;
token = este.string.stripSlashHashPrefixes(e.token);
if (this.ignoreNextOnHistoryNavigate) {
this.ignoreNextOnHistoryNavigate = false;
return;
}
return this.processRoutes(token, e.isNavigation);
};
/**
@param {goog.events.BrowserEvent} e
@return {string}
@protected
*/
este.Router.prototype.tryGetToken = function(e) {
var token;
token = '';
goog.dom.getAncestor(e.target, (function(_this) {
return function(node) {
var ref, ref1;
if (!goog.dom.isElement(node)) {
return false;
}
if (node.hasAttribute('e-router-ignore') || node.hasAttribute('data-e-router-ignore')) {
return true;
}
if (((ref = node.getAttribute('href')) != null ? ref.indexOf('http') : void 0) === 0) {
return true;
}
if (((ref1 = node.getAttribute('href')) != null ? ref1.indexOf('//') : void 0) === 0) {
return true;
}
token = goog.string.trim(node.getAttribute('href') || '');
return !!token;
};
})(this), true);
return token;
};
/**
@param {goog.events.BrowserEvent} e
@return {boolean}
@protected
*/
este.Router.prototype.isBackButton = function(e) {
var ref;
if (((ref = goog.global.history) != null ? ref.length : void 0) === 1) {
return false;
}
return !!goog.dom.getAncestor(e.target, (function(_this) {
return function(node) {
if (!goog.dom.isElement(node)) {
return false;
}
return node.hasAttribute('e-router-back-button') || node.hasAttribute('data-e-router-back-button');
};
})(this), true);
};
/**
@param {string} token
@param {boolean} isNavigation
@protected
*/
este.Router.prototype.processRoutes = function(token, isNavigation) {
var e, error, firstRouteMatched, i, len, matched, ref, route;
token = este.string.stripSlashHashPrefixes(token);
firstRouteMatched = false;
ref = this.routes;
for (i = 0, len = ref.length; i < len; i++) {
route = ref[i];
try {
matched = route.process(token, isNavigation, firstRouteMatched);
if (matched) {
firstRouteMatched = true;
}
} catch (error) {
e = error;
} finally {
continue;
}
}
};
/**
@override
*/
este.Router.prototype.disposeInternal = function() {
this.history.dispose();
this.gestureHandler.dispose();
este.Router.superClass_.disposeInternal.call(this);
};