UNPKG

este-library-oldschool

Version:

Library for github.com/steida/este.git

328 lines (297 loc) 8.4 kB
// 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); };