UNPKG

prodio

Version:

Simplified project management

216 lines (189 loc) 5.13 kB
(function(w, d) { /* * * Satnav.js Routing * @author: Joe Harlow * */ 'use strict'; var _sN, _otherwise, _promise, _current, _params = {}, _ = {}, _routes = [], _listeners = {}, _settings = { poll : 25, // hashchange Polyfill poll interval html5 : false, // use pushState if available force : false // force hashchange events }; _.setup = function(fn) { var ev = _settings.html5 && 'pushState' in w.history ? 'popstate' : 'hashchange'; if (_settings.force) _.force(); if (('on' + ev) in w) { _.listen(w, ev, fn); } else { var oH = w.location.href; setInterval(function() { var nH = w.location.href; if (oH !== nH) { oH = nH; fn.call(w); } }, _settings.poll); } }; _.force = function() { var a = d.getElementsByTagName('a'); var _click = function() { var href = this.getAttribute('href'); var s = _.sanitize(href.substr(href.indexOf('#') + 1)); if (s === _current) { _.change(); } }; for (var i = 0; i < a.length; i++) { var el = a[i]; if (el.hasAttribute('href') && el.getAttribute('href').indexOf('#') !== -1) { _.listen(el, 'click', _click); } } }; _.promise = function() { var _t = this; _t.stack = []; _t.then = function(fn) { _t.stack.push(fn); }; _t.resolve = function() { while (_t.stack.length) { return _t.stack.shift().call(_sN); } }; }; _.route = function(path, directions) { var params = []; path = '^' + path; path = path.replace(/(\?)?\{([^}]+)\}/g, function(match, optional, param) { params.push({ name : param, optional : (typeof optional !== 'undefined') ? true : false }); return '([\\w-.]+)?'; }).replace(/\//g, '\\/?'); path += '\\/?$'; _routes.push({ regex : new RegExp(path), params : params, directions : directions }); }; _.change = function() { _current = _.state(); var route = _.match(_current); if (route) { var old = _params; _params = route.params; var change = _.dispatch('change', _params, old); if (_settings.html5 && 'pushState' in w.history) { w.history.replaceState(_.extend(_params, { hash : _current }), '{Satnav}', '/' + _current); } var end = function() { route.directions.call(_sN, _params); }; if (change instanceof _.promise) { _promise.then(end); } else { end(); } } else { w.location.hash = _otherwise || ''; } }; _.sanitize = function(path) { return path.replace(/^\//g, ''); }; _.match = function(h) { for (var r in _routes) { var route = _routes[r]; var matched = h.match(route.regex); if (matched) { var p = {}; for (var i = 1, len = matched.length; i < len; i++) { if (typeof matched[i] === 'undefined' && !route.params[i-1].optional) { throw new Error('[Satnav] A required route parameter was not defined'); } else if (typeof matched[i] !== 'undefined') { p[route.params[i-1].name] = matched[i]; } } return { directions : route.directions, params : p }; } } return undefined; }; _.dispatch = function(ev) { var args = Array.prototype.slice.call(arguments, 1); return ev in _listeners && _listeners[ev].apply(_sN, args); }; _.state = function() { return w.history.state && w.history.state.hash ? w.history.state.hash : _.sanitize(w.location.hash.substr(1)); }; _.extend = function(obj, ext) { for (var prop in ext) { obj[prop] = (obj.hasOwnProperty(prop)) ? obj[prop] : ext[prop]; } return obj; }; _.listen = (function() { if (w.addEventListener) { return function(el, ev, fn) { el.addEventListener(ev, fn, false); }; } else if (w.attachEvent) { return function(el, ev, fn) { el.attachEvent('on' + ev, function() { fn.call(el); }, false); }; } })(); _sN = function(config) { _settings = _.extend(config, _settings); return _sN; }; _sN.defer = (function() { _promise = new _.promise(); return _promise; })(); _sN.resolve = function() { setTimeout(_promise.resolve, 1); }; _sN.navigate = function(route) { if ('path' in route) { if (_routes.length === 0) { _.setup(_.change); } _.route(_.sanitize(route.path), ('directions' in route) ? route.directions : function() {}); } else { throw new Error('[Satnav] A required argument was not defined'); } return _sN; }; _sN.otherwise = function(route) { _otherwise = (typeof route === 'string') ? _.sanitize(route) : ('path' in route) ? _.sanitize(route.path) : ''; return _sN; }; _sN.change = function(fn) { _listeners.change = fn; return _sN; }; _sN.go = function() { _.change(); return _sN; }; if (typeof w.define === 'function' && w.define.amd) { w.define('Satnav', [], function() { return _sN; }); } else if (typeof w.module !== 'undefined' && w.module.exports) { w.module.exports = _sN; } else { w.sN = w.Satnav = _sN; } })(window, document);