├── .gitignore ├── .travis.yml ├── window.js ├── LICENSE ├── package.json ├── document.js ├── index.html ├── dist ├── bundle.js ├── bundle.web.min.js └── bundle.web.js ├── README.md ├── index.js └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .c9/ 3 | *.log -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 'stable' 4 | - '5' 5 | - '4' -------------------------------------------------------------------------------- /window.js: -------------------------------------------------------------------------------- 1 | // mock window object for tests 2 | 3 | // should satisfy 4 | // - window.RouterInstance_ 5 | // - window.addEventListener 6 | // - window.location 7 | // - window.history.pushState 8 | // - window.requestAnimationFrame 9 | 10 | const EventEmitter = require('events') 11 | const inherits = require('inherits') 12 | const sinon = require('sinon') 13 | 14 | function Window () { 15 | this.location = { 16 | pathname: '' 17 | } 18 | this.history = { 19 | pushState: sinon.spy() 20 | } 21 | } 22 | 23 | inherits(Window, EventEmitter) 24 | 25 | Window.prototype.addEventListener = Window.prototype.on 26 | Window.prototype.requestAnimationFrame = function (cb) { 27 | var $this = window.RouterInstance_ || {} 28 | cb.call($this) 29 | } 30 | 31 | module.exports = new Window() 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 YerkoPalma 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "singleton-router", 3 | "version": "2.0.1", 4 | "description": "Tiny singleton router for the browser", 5 | "main": "index.js", 6 | "repository": "YerkoPalma/singleton-router", 7 | "files": [ 8 | "index.js", 9 | "dist/bundle.js", 10 | "dist/bundle.web.js", 11 | "dist/bundle.web.min.js" 12 | ], 13 | "scripts": { 14 | "test": "standard --verbose | snazzy && tape test.js | tap-summary", 15 | "build": "mkdir -p dist && squash index.js > dist/bundle.js && npm run build:standalone", 16 | "build:standalone": "mkdir -p dist && browserify -e index.js -o dist/bundle.web.js -s RouterSingleton && squash dist/bundle.web.js > dist/bundle.web.min.js", 17 | "start": "budo dist/bundle.js:app.js --verbose --pushstate -p 8080 -H '0.0.0.0' -- -t es2040 -t rollupify -s RouterSingleton" 18 | }, 19 | "standard": { 20 | "ignore": [ 21 | "dist/" 22 | ] 23 | }, 24 | "keywords": [ 25 | "router", 26 | "client-router", 27 | "browser", 28 | "web", 29 | "singleton" 30 | ], 31 | "author": "Yerko Palma ", 32 | "license": "MIT", 33 | "dependencies": { 34 | "url-pattern": "^1.0.3" 35 | }, 36 | "devDependencies": { 37 | "browserify": "^14.1.0", 38 | "budo": "^10.0.3", 39 | "butternut": "^0.4.5", 40 | "global": "^4.3.1", 41 | "inherits": "^2.0.3", 42 | "sinon": "^2.1.0", 43 | "snazzy": "^7.0.0", 44 | "standard": "^10.0.2", 45 | "tap-summary": "^3.0.1", 46 | "tape": "^4.6.3" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /document.js: -------------------------------------------------------------------------------- 1 | // mock for document object for tests 2 | 3 | // should satisfy 4 | // - document.querySelectorAll Used to get all the links with [data-route] attribute 5 | // - document.querySelector Used to mount the root element (expect only '#app', '' or undefined) 6 | // - document.body Used as default root element 7 | // - Element.hasChildNodes Used in manageState 8 | // - Element.appendChild Used in manageState 9 | // - Element.replaceChild Used in manageState 10 | // - Element.firstChild Used in manageState 11 | 12 | var document = require('global/document') 13 | const inherits = require('inherits') 14 | const EventEmitter = require('events') 15 | const sinon = require('sinon') 16 | 17 | function Element () {} 18 | inherits(Element, EventEmitter) 19 | 20 | Element.prototype.addEventListener = Element.prototype.on 21 | Element.prototype.hasChildNodes = function (fn) { return typeof fn === 'function' && fn() } 22 | Element.prototype.appendChild = sinon.spy() 23 | Element.prototype.replaceChild = sinon.spy() 24 | 25 | var emitter = new Element() 26 | 27 | document.querySelector = function (selector) { 28 | emitter.selector = selector 29 | return emitter 30 | } 31 | document.querySelectorAll = function (selector) { 32 | return [document.querySelector(selector), document.querySelector(selector)] 33 | } 34 | document.body = document.querySelector('body') 35 | /* document.body = html`` 36 | document.createElement = html.createElement 37 | */ 38 | 39 | module.exports = document 40 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | 18 |
19 | Home 20 | Users 21 | Profile 22 | Anchor 23 | Anchor 24 |
25 |
26 |
27 | 28 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /dist/bundle.js: -------------------------------------------------------------------------------- 1 | var UrlPattern=require('url-pattern'),assert=require('assert');function getRouter(a){if(typeof window.RouterInstance!=='undefined'){return window.RouterInstance}window.RouterInstance=new Router(a);return window.RouterInstance}function Router(a){this.routes={};this.currentPath=null;this.previousPath=null;this.currentRoute=null;this.previousRoute=null;this.root=null;this.firstRender=!0;this.rootEl=null;this.store=null;this.default=null;this._debug=!1;this.onRender=a&&a.onRender?a.onRender:null;this.onNavClick=a&&a.onNavClick?a.onNavClick:null;this.prefix=a&&a.prefix?a.prefix:'';var b=this;window.addEventListener('popstate',function(a){b.onPopState(a)});var c=document.querySelectorAll('[data-route]');Array.prototype.forEach.call(c,function(a){a.addEventListener('click',function(c){c.preventDefault();a.setAttribute('data-bind',+new Date);b.goToPath(a.getAttribute('data-route'));typeof b.onNavClick==='function'&&b.onNavClick(a.getAttribute('data-route'),a)})})}Router.prototype.setStore=function(a){this.store=a};Router.prototype.addRoute=function(a,b,c){assert.equal(typeof a,'string');assert.equal(typeof b,'function');assert(typeof c==='undefined'||typeof c==='function');this.routes[a]=new Route(a,b,c)};Router.prototype.setRoot=function(a){a=a||'/';this.root=this.getRoute(a);this.root||(this.addRoute('/',function(){return document.createElement('div')}),this.root=this.routes['/'])};Router.prototype.start=function(a){assert(typeof a==='undefined'||typeof a==='string');assert(Object.keys(this.routes).length>0);assert.notEqual(this.root,null);this.rootEl=document.querySelector(a)||document.body;this.requestStateUpdate()};Router.prototype.onPopState=function(a){a&&a.preventDefault();this.requestStateUpdate(a)};Router.prototype.getRoute=function(a){assert.equal(typeof a,'string');var b=[];for(var c in this.routes)this.routes.hasOwnProperty(c)&&(this.routes[c]._urlPattern.match(a)!==null&&b.push(this.routes[c]));if(b.length===0)return null;return b[0]};Router.prototype.notFound=function(a){assert.equal(typeof a,'function');this.default=new Route(null,a)};Router.prototype.goToPath=function(a,b){b=b||null;if(a===window.location.pathname){return}var c=this;this.previousPath=window.location.pathname;this.currentPath=a;this.previousRoute=this.currentRoute||this.root;this.currentRoute=this.getRoute(this.currentPath);window.history.pushState(void 0,b,a);window.requestAnimationFrame(function(){c.manageState()})};Router.prototype.manageState=function(){var a=this;if(this.currentPath===this.previousPath)return;!this.currentRoute&&this.default&&(this.currentRoute=this.default);var b=this.currentRoute.onStart(this.store),c=null;this.firstRender&&b?(this.firstRender=!1,this.rootEl.appendChild(b)):!this.onRender||typeof this.onRender!=='function'?(c=this.rootEl.lasttElementChild||this.rootEl.lastChild,this.rootEl.replaceChild(b,c)):(c=this.rootEl.lasttElementChild||this.rootEl.lastChild,this.onRender(b,c,this.currentRoute.cb));var d=document.querySelectorAll('a[data-route]');Array.prototype.forEach.call(d,function(b){b.addEventListener('click',function(c){c.preventDefault();b.getAttribute('data-bind')||a.goToPath(b.getAttribute('data-route'));typeof a.onNavClick==='function'&&a.onNavClick(b.getAttribute('data-route'),b)})});if((!this.onRender||typeof this.onRender!=='function')&&typeof this.currentRoute.cb==='function'){try{this.currentRoute.cb()}catch(a){console.error(a)}}};Router.prototype.requestStateUpdate=function(a){this.previousPath=this.currentPath;this.currentPath=window.location.pathname;this.currentRoute=this.getRoute(a&&a.target!==window?a.target.getAttribute('data-route'):window.location.pathname);var b=this;window.requestAnimationFrame(function(){b.manageState()})};function Route(a,b,c){this.pattern=a;this.cb=c;this._urlPattern=a?new UrlPattern(a):null;this.view=b;this.params=null;this.path=null}Route.prototype.getParams=function(){if(!this.path)return!1;return this.params};Route.prototype.setParams=function(){if(!this.path)return!1;this.params=this._urlPattern?this._urlPattern.match(this.path):null};Route.prototype.onStart=function(a){this.path=window.location.pathname;this.params=this._urlPattern?this._urlPattern.match(this.path):null;return this.view(this.params,a)};module.exports=getRouter 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

singleton-router

2 |
3 |
4 | 5 | 6 | API stability 8 | 9 | 10 | 11 | Standard 13 | 14 | 15 | 16 | Build Status 18 | 19 |
20 | 21 | ## Install 22 | 23 | ```bash 24 | $ npm install --save singleton-router 25 | ``` 26 | 27 | or 28 | 29 | ```html 30 | 31 | ``` 32 | 33 | ## Usage 34 | 35 | ```js 36 | import SingletonRouter from 'singleton-router' 37 | 38 | const router = SingletonRouter() 39 | // each view is a function that returns an htmlElement object 40 | router.addRoute('/', HomeView) 41 | // callback is fired after new element (view) is mounted 42 | router.addRoute('/about', AboutView, () => { console.log('Content mounted to DOM') }) 43 | router.addRoute('/user/:id', UserView) 44 | // starting route 45 | router.setRoot('/') 46 | // element to mount router, if not set will mount on body element 47 | router.start('#app') 48 | ``` 49 | 50 | In your html, any clickeable element with an attribute `data-route` would navigate to the route specified there. So, for example, an anchor tag like `about` will navigate to the `AboutView`. 51 | Programatic navigation can be done with the [`router.goToPath`](#routergotopathpath--title) function. 52 | 53 | ## API 54 | 55 | ### const router = SingletonRouter([options]) 56 | 57 | There is a single function exposed, the function returns the instance of the router. The instance is also saved to the global window object as `RouterInstance_`. It accepts an optional `options` object, the availaible options are: 58 | 59 | - onRender(currentView, previousView, cb): By default, the router use the [`replaceChild`](https://developer.mozilla.org/en-US/docs/Web/API/Node/replaceChild) function to render the view, you can replace it to add animations, or use some html diffing function to improve performance. Notice that the function is ran inside a [`requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) call, so don't need to include it yourself. Also, when defined, you get a third parameter, that's the callback defined on each `addRoute` method. 60 | - onNavClick(path, element): If provided, is executed after any element with the `data-route` attribute in it. Useful to mark `active` links in navigation menus. 61 | 62 | ### router.setStore(store) 63 | 64 | Set a store container, like [redux](https://github.com/reactjs/redux) for example. This store is passed to each view. 65 | 66 | ### router.addRoute(path, view [, callback]) 67 | 68 | Add routes to the router. The arguments are: 69 | 70 | - `path`: A string with the path of the route. 71 | - `view`: A function that returns an HtmlElement, the function hast two arguments: 72 | - `params`: The value of the params for that route. 73 | - `store`: the store object set before by `router.setStore`. 74 | - `callback`: Optional function that runs after the view is rendered to the DOM. Note that if you define a `onRender` function, then you MUST handle yourself this callback. 75 | 76 | ### router.setRoot(path) 77 | 78 | Set a starting route 79 | 80 | ### router.goToPath(path [, title]) 81 | 82 | Programmatically navigates to a route. Optionally add a title for the `history` api. 83 | 84 | ### router.start([selector]) 85 | 86 | Start the router. This function receive a selector to mount the app, if none is provided, it will replace and update the `body` od the document. 87 | 88 | ## Licencia 89 | 90 | [MIT](/license) © [Yerko Palma](https://github.com/YerkoPalma). 91 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var UrlPattern = require('url-pattern') 2 | var assert = require('assert') 3 | 4 | function getRouter (options) { 5 | if (typeof window.RouterInstance !== 'undefined') { 6 | return window.RouterInstance 7 | } 8 | 9 | window.RouterInstance = new Router(options) 10 | 11 | return window.RouterInstance 12 | } 13 | 14 | function Router (options) { 15 | this.routes = {} 16 | this.currentPath = null 17 | this.previousPath = null 18 | this.currentRoute = null 19 | this.previousRoute = null 20 | this.root = null 21 | this.firstRender = true 22 | this.rootEl = null 23 | this.store = null 24 | this.default = null 25 | this._debug = false 26 | this.onRender = options && options.onRender ? options.onRender : null 27 | this.onNavClick = options && options.onNavClick ? options.onNavClick : null 28 | this.prefix = options && options.prefix ? options.prefix : '' 29 | var self = this 30 | 31 | window.addEventListener('popstate', function (e) { 32 | self.onPopState(e) 33 | }) 34 | 35 | var links = document.querySelectorAll('[data-route]') 36 | Array.prototype.forEach.call(links, function (link) { 37 | link.addEventListener('click', function (event) { 38 | event.preventDefault() 39 | link.setAttribute('data-bind', +new Date()) 40 | self.goToPath(link.getAttribute('data-route')) 41 | if (typeof self.onNavClick === 'function') self.onNavClick(link.getAttribute('data-route'), link) 42 | }) 43 | }) 44 | } 45 | 46 | Router.prototype.setStore = function setStore (store) { 47 | this.store = store 48 | } 49 | 50 | Router.prototype.addRoute = function addRoute (pattern, view, cb) { 51 | assert.equal(typeof pattern, 'string') 52 | assert.equal(typeof view, 'function') 53 | assert(typeof cb === 'undefined' || typeof cb === 'function') 54 | 55 | this.routes[pattern] = new Route(pattern, view, cb) 56 | } 57 | 58 | Router.prototype.setRoot = function setRoot (path) { 59 | path = path || '/' 60 | this.root = this.getRoute(path) 61 | if (!this.root) { 62 | this.addRoute('/', function () { return document.createElement('div') }) 63 | this.root = this.routes['/'] 64 | } 65 | } 66 | 67 | Router.prototype.start = function start (selector) { 68 | assert(typeof selector === 'undefined' || typeof selector === 'string') 69 | assert(Object.keys(this.routes).length > 0) 70 | assert.notEqual(this.root, null) 71 | this.rootEl = document.querySelector(selector) || document.body 72 | this.requestStateUpdate() 73 | } 74 | 75 | Router.prototype.onPopState = function onPopState (e) { 76 | if (e) e.preventDefault() 77 | this.requestStateUpdate(e) 78 | } 79 | 80 | Router.prototype.getRoute = function getRoute (path) { 81 | assert.equal(typeof path, 'string') 82 | var posibleRoutes = [] 83 | for (var pattern in this.routes) { 84 | if (this.routes.hasOwnProperty(pattern)) { 85 | if (this.routes[pattern]._urlPattern.match(path) !== null) posibleRoutes.push(this.routes[pattern]) 86 | } 87 | } 88 | // there are more than one candidate 89 | if (posibleRoutes.length === 0) return null 90 | return posibleRoutes[0] 91 | } 92 | 93 | Router.prototype.notFound = function notFound (notFoundView) { 94 | assert.equal(typeof notFoundView, 'function') 95 | this.default = new Route(null, notFoundView) 96 | } 97 | 98 | Router.prototype.goToPath = function goToPath (path, title) { 99 | title = title || null 100 | // Only process real changes. 101 | if (path === window.location.pathname) { 102 | return 103 | } 104 | var self = this 105 | this.previousPath = window.location.pathname 106 | this.currentPath = path 107 | this.previousRoute = this.currentRoute || this.root 108 | this.currentRoute = this.getRoute(this.currentPath) 109 | // assert(this.currentRoute) 110 | 111 | window.history.pushState(undefined, title, path) 112 | window.requestAnimationFrame(function () { 113 | self.manageState() 114 | }) 115 | } 116 | 117 | Router.prototype.manageState = function manageState () { 118 | var self = this 119 | if (this.currentPath === this.previousPath) return 120 | if (!this.currentRoute && this.default) this.currentRoute = this.default 121 | // currentView is the new view to be added 122 | var currentView = this.currentRoute.onStart(this.store) 123 | var child = null 124 | 125 | // if (!this.rootEl.hasChildNodes(currentView)) { 126 | if (this.firstRender && currentView) { 127 | this.firstRender = false 128 | this.rootEl.appendChild(currentView) 129 | } else if (!this.onRender || typeof this.onRender !== 'function') { 130 | child = this.rootEl.lasttElementChild || this.rootEl.lastChild 131 | this.rootEl.replaceChild(currentView, child) 132 | } else { 133 | child = this.rootEl.lasttElementChild || this.rootEl.lastChild 134 | this.onRender(currentView, child, this.currentRoute.cb) 135 | } 136 | var links = document.querySelectorAll('a[data-route]') 137 | Array.prototype.forEach.call(links, function (link) { 138 | link.addEventListener('click', function (e) { 139 | e.preventDefault() 140 | if (!link.getAttribute('data-bind')) self.goToPath(link.getAttribute('data-route')) 141 | if (typeof self.onNavClick === 'function') self.onNavClick(link.getAttribute('data-route'), link) 142 | }) 143 | }) 144 | if ((!this.onRender || typeof this.onRender !== 'function') && typeof this.currentRoute.cb === 'function') { 145 | try { 146 | this.currentRoute.cb() 147 | } catch (ex) { 148 | console.error(ex) 149 | } 150 | } 151 | } 152 | 153 | Router.prototype.requestStateUpdate = function requestStateUpdate (e) { 154 | this.previousPath = this.currentPath 155 | this.currentPath = window.location.pathname 156 | this.currentRoute = this.getRoute(e && e.target !== window 157 | ? e.target.getAttribute('data-route') 158 | : window.location.pathname) 159 | var self = this 160 | window.requestAnimationFrame(function () { 161 | self.manageState() 162 | }) 163 | } 164 | 165 | function Route (pattern, view, cb) { 166 | this.pattern = pattern 167 | this.cb = cb 168 | this._urlPattern = pattern ? new UrlPattern(pattern) : null 169 | this.view = view 170 | this.params = null 171 | this.path = null 172 | } 173 | 174 | Route.prototype.getParams = function getParams () { 175 | if (!this.path) return false 176 | return this.params 177 | } 178 | Route.prototype.setParams = function setParams () { 179 | if (!this.path) return false 180 | this.params = this._urlPattern ? this._urlPattern.match(this.path) : null 181 | } 182 | 183 | Route.prototype.onStart = function onStart (store) { 184 | this.path = window.location.pathname 185 | this.params = this._urlPattern ? this._urlPattern.match(this.path) : null 186 | return this.view(this.params, store) 187 | } 188 | 189 | module.exports = getRouter 190 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-global-assign */ 2 | /* eslint-disable no-native-reassign */ 3 | /* eslint-disable no-unused-vars */ 4 | 5 | var test = require('tape') 6 | var assert = require('assert') 7 | 8 | global.window = require('./window') 9 | global.document = require('./document') 10 | var SingletonRouter = require('./') 11 | var sinon = require('sinon') 12 | var router = null 13 | 14 | test = beforeEach(test, function (t) { 15 | // called before each thing 16 | window.RouterInstance = undefined 17 | router = undefined 18 | router = SingletonRouter() 19 | 20 | // when done call 21 | t.end() 22 | }) 23 | 24 | test('singleton pattern', function (t) { 25 | t.plan(1) 26 | 27 | router.something = {} 28 | var router_ = SingletonRouter() 29 | t.deepEqual(router, router_) 30 | }) 31 | 32 | test('Router', function (t) { 33 | t.test('window should handle popstate', function (t) { 34 | t.plan(1) 35 | 36 | var events = window.listeners('popstate') 37 | t.ok(events) 38 | }) 39 | 40 | t.test('setStore should set the store property', function (t) { 41 | t.plan(2) 42 | 43 | t.equal(router.store, null) 44 | var store = {} 45 | router.setStore(store) 46 | t.equal(router.store, store) 47 | }) 48 | 49 | t.test('addRoute', function (t) { 50 | t.plan(3) 51 | 52 | var routes = Object.keys(router.routes) 53 | t.equal(routes.length, 0) 54 | router.addRoute('/r', function () {}) 55 | routes = Object.keys(router.routes) 56 | t.equal(routes.length, 1) 57 | t.ok(router.routes['/r']) 58 | }) 59 | 60 | t.test('addRoute validations', function (t) { 61 | t.plan(6) 62 | 63 | // add Route should require pattern and view 64 | t.throws(function () { router.addRoute() }) 65 | t.throws(function () { router.addRoute(null, null) }) 66 | // pattern must be a string and view a function 67 | t.throws(function () { router.addRoute({}, {}) }) 68 | t.doesNotThrow(function () { router.addRoute('', function () {}) }) 69 | // if callback is present, it must be a function 70 | t.throws(function () { router.addRoute('', function () {}, '') }) 71 | t.doesNotThrow(function () { router.addRoute('', function () {}, function () {}) }) 72 | }) 73 | 74 | t.test('setRoot validations', function (t) { 75 | t.plan(3) 76 | 77 | // path undefined or string 78 | router.addRoute('/', function () {}) 79 | t.throws(function () { router.setRoot(null) }) 80 | t.throws(function () { router.setRoot({}) }) 81 | // if string path inserted, must be a valid route 82 | t.throws(function () { router.setRoot('/fake') }) 83 | }) 84 | }) 85 | 86 | test('setRoot', function (t) { 87 | t.plan(5) 88 | 89 | router.addRoute('/root', function () {}) 90 | // with a string, search for a route with that path, otherwiese throw 91 | t.doesNotThrow(function () { router.setRoot('/root') }) 92 | t.equal(router.root, router.routes['/root']) 93 | // with undefined, creates a route on path '/' 94 | t.doesNotThrow(function () { router.setRoot() }) 95 | var routes = Object.keys(router.routes) 96 | t.equal(routes.length, 2) 97 | t.equal(router.root, router.routes['/']) 98 | }) 99 | 100 | test('start', function (t) { 101 | t.plan(3) 102 | 103 | router.addRoute('/', function () {}) 104 | router.setRoot('/') 105 | router.requestStateUpdate = sinon.spy() 106 | // without selector 107 | router.start() 108 | t.equal(router.rootEl, document.body) 109 | // with selector 110 | router.start('#app') 111 | t.equal(router.rootEl, document.querySelector('#app')) 112 | // should call requestStateUpdate 113 | t.equal(router.requestStateUpdate.callCount, 2) 114 | }) 115 | 116 | test('start validations', function (t) { 117 | t.plan(4) 118 | router.requestStateUpdate = sinon.spy() 119 | // there must be at least one route defined 120 | // root route must be set 121 | t.throws(function () { router.start() }) 122 | router.addRoute('/', function () {}) 123 | router.setRoot('/') 124 | t.doesNotThrow(function () { router.start() }) 125 | // selector must be undefined or a string 126 | t.throws(function () { router.start(null) }) 127 | t.throws(function () { router.start({}) }) 128 | }) 129 | 130 | test('onPopState', function (t) { 131 | t.plan(1) 132 | router.requestStateUpdate = sinon.spy() 133 | router.onPopState() 134 | // window.emit('popstate', { preventDefault: () => {} }) 135 | t.equal(router.requestStateUpdate.callCount, 1) 136 | }) 137 | 138 | test('getRoute', function (t) { 139 | t.plan(6) 140 | // path must always be a string 141 | t.throws(function () { router.getRoute() }) 142 | t.throws(function () { router.getRoute(null) }) 143 | t.throws(function () { router.getRoute({}) }) 144 | // if there is no route in router, getRoute always return null 145 | t.equal(router.getRoute(''), null) 146 | t.equal(router.getRoute('/'), null) 147 | // if there is any route, return the best match 148 | router.addRoute('/', function () { t.fail() }) 149 | router.addRoute('/post', function () { t.pass() }) 150 | router.addRoute('/post/:id', function () { t.fail() }) 151 | var route = router.getRoute('/post') 152 | route.view() 153 | }) 154 | 155 | test('notFound', function (t) { 156 | t.plan(7) 157 | // notFoundView should be a function 158 | t.throws(function () { router.notFound() }) 159 | t.throws(function () { router.notFound(null) }) 160 | t.throws(function () { router.notFound({}) }) 161 | t.throws(function () { router.notFound('') }) 162 | // this.default should be defined 163 | t.equal(router.default, null) 164 | router.notFound(function () {}) 165 | t.notEqual(router.defualt, null) 166 | // should not add anything to the routes object 167 | t.equal(Object.keys(router.routes).length, 0) 168 | }) 169 | 170 | test('goToPath', function (t) { 171 | t.plan(7) 172 | // for the same path don't do anything 173 | t.equal(router.currentPath, null) 174 | window.location.pathname = '/' 175 | router.goToPath('/') 176 | t.equal(router.currentPath, null) 177 | // should throw if there is no route match 178 | // t.throws(() => { router.goToPath('/route') }, assert.AssertionError) 179 | // should call manage state 180 | router.manageState = sinon.spy() 181 | router.addRoute('/', function () {}) 182 | router.addRoute('/route', function () {}) 183 | router.setRoot('/') 184 | router.goToPath('/route') 185 | t.equal(router.manageState.callCount, 1) 186 | // should update currentPath 187 | t.equal(router.currentPath, '/route') 188 | // should update previousPath 189 | t.equal(router.previousPath, '/') 190 | // should update previousRoute 191 | t.equal(router.previousRoute, router.getRoute('/')) 192 | // should update currentRoute 193 | t.equal(router.currentRoute, router.getRoute('/route')) 194 | }) 195 | 196 | test('manageState', function (t) { 197 | t.plan(3) 198 | // should do nothing when is the same path 199 | router.addRoute('/foo', function () {}) 200 | router.addRoute('/bar', function () {}) 201 | router.notFound(function () { return true }) 202 | router.setRoot('/foo') 203 | router.start() 204 | var preRouter = router 205 | router.manageState() 206 | t.equal(preRouter, router) 207 | router.previousPath = '/foo' 208 | router.currentPath = '/bar' 209 | router.manageState() 210 | // when there is no currentRoute, set to default if present 211 | t.ok(router.rootEl.appendChild.calledWith(router.default.view())) 212 | // if rootEl has no childs, append view 213 | t.ok(router.rootEl.appendChild.calledOnce) 214 | // if rootEl has childs, replace with view 215 | // if router define custom render function, call that 216 | // currentRoute should call cb if defined 217 | }) 218 | 219 | test('request state update', function (t) { 220 | t.plan(3) 221 | 222 | router.manageState = sinon.spy() 223 | router.addRoute('/foo', function () {}) 224 | router.addRoute('/', function () {}) 225 | router.setRoot('/') 226 | router.start() 227 | window.location.pathname = '/foo' 228 | router.currentPath = '/' 229 | router.requestStateUpdate() 230 | t.equal(router.previousPath, '/') 231 | t.equal(router.currentPath, '/foo') 232 | t.ok(router.manageState.calledTwice) 233 | }) 234 | 235 | test('onRender', function (t) { 236 | t.plan(2) 237 | 238 | var onRender = function (previous, current, _cb) { 239 | cb() 240 | t.pass() 241 | } 242 | var cb = function () { t.pass() } 243 | window.RouterInstance = undefined 244 | router = undefined 245 | router = SingletonRouter({ onRender: onRender }) 246 | 247 | router.addRoute('/foo', function () {}, cb) 248 | router.addRoute('/', function () {}) 249 | router.setRoot('/') 250 | router.start() 251 | router.goToPath('/foo') 252 | }) 253 | 254 | function beforeEach (test, handler) { 255 | return function tapish (name, listener) { 256 | test(name, function (assert) { 257 | var _end = assert.end 258 | assert.end = function () { 259 | assert.end = _end 260 | listener(assert) 261 | } 262 | 263 | handler(assert) 264 | }) 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /dist/bundle.web.min.js: -------------------------------------------------------------------------------- 1 | !function(a){if(typeof exports==="object"&&typeof module!=="undefined")module.exports=a();else if(typeof define==="function"&&define.amd)define([],a);else{var b;typeof window!=="undefined"?(b=window):typeof global!=="undefined"?(b=global):typeof self!=="undefined"?(b=self):(b=this);b.RouterSingleton=a()}}(function(){var a;return(function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i=typeof require=="function"&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}var f=typeof require=="function"&&require;for(var g=0;g0);e.notEqual(this.root,null);this.rootEl=document.querySelector(a)||document.body;this.requestStateUpdate()};g.prototype.onPopState=function(a){a&&a.preventDefault();this.requestStateUpdate(a)};g.prototype.getRoute=function(a){e.equal(typeof a,'string');var b=[];for(var c in this.routes)this.routes.hasOwnProperty(c)&&(this.routes[c]._urlPattern.match(a)!==null&&b.push(this.routes[c]));if(b.length===0)return null;return b[0]};g.prototype.notFound=function(a){e.equal(typeof a,'function');this.default=new h(null,a)};g.prototype.goToPath=function(a,b){b=b||null;if(a===window.location.pathname){return}var c=this;this.previousPath=window.location.pathname;this.currentPath=a;this.previousRoute=this.currentRoute||this.root;this.currentRoute=this.getRoute(this.currentPath);window.history.pushState(void 0,b,a);window.requestAnimationFrame(function(){c.manageState()})};g.prototype.manageState=function(){var a=this;if(this.currentPath===this.previousPath)return;!this.currentRoute&&this.default&&(this.currentRoute=this.default);var b=this.currentRoute.onStart(this.store),c=null;this.firstRender&&b?(this.firstRender=!1,this.rootEl.appendChild(b)):!this.onRender||typeof this.onRender!=='function'?(c=this.rootEl.lasttElementChild||this.rootEl.lastChild,this.rootEl.replaceChild(b,c)):(c=this.rootEl.lasttElementChild||this.rootEl.lastChild,this.onRender(b,c,this.currentRoute.cb));var d=document.querySelectorAll('a[data-route]');Array.prototype.forEach.call(d,function(b){b.addEventListener('click',function(c){c.preventDefault();b.getAttribute('data-bind')||a.goToPath(b.getAttribute('data-route'));typeof a.onNavClick==='function'&&a.onNavClick(b.getAttribute('data-route'),b)})});if((!this.onRender||typeof this.onRender!=='function')&&typeof this.currentRoute.cb==='function'){try{this.currentRoute.cb()}catch(a){console.error(a)}}};g.prototype.requestStateUpdate=function(a){this.previousPath=this.currentPath;this.currentPath=window.location.pathname;this.currentRoute=this.getRoute(a&&a.target!==window?a.target.getAttribute('data-route'):window.location.pathname);var b=this;window.requestAnimationFrame(function(){b.manageState()})};function h(a,b,c){this.pattern=a;this.cb=c;this._urlPattern=a?new d(a):null;this.view=b;this.params=null;this.path=null}h.prototype.getParams=function(){if(!this.path)return!1;return this.params};h.prototype.setParams=function(){if(!this.path)return!1;this.params=this._urlPattern?this._urlPattern.match(this.path):null};h.prototype.onStart=function(a){this.path=window.location.pathname;this.params=this._urlPattern?this._urlPattern.match(this.path):null;return this.view(this.params,a)};b.exports=f},{"assert":2,"url-pattern":4}],2:[function(a,b,c){(function(c){"use strict";function d(a,b){if(a===b){return 0}var c=a.length,d=b.length;for(var e=0,f=Math.min(c,d);e=0){var g=d.indexOf('\n',f+1);d=d.substring(g+1)}this.stack=d}}};f.inherits(l.AssertionError,Error);function o(a,b){if(typeof a==='string'){return a.length=0;l--){if(i[l]!==j[l])return!1}for(l=i.length-1;l>=0;l--){k=i[l];if(!t(a[k],b[k],c,d))return!1}return!0}l.notDeepEqual=function(a,b,c){t(a,b,!1)&&r(a,b,c,'notDeepEqual',l.notDeepEqual)};l.notDeepStrictEqual=w;function w(a,b,c){t(a,b,!0)&&r(a,b,c,'notDeepStrictEqual',w)}l.strictEqual=function(a,b,c){a!==b&&r(a,b,c,'===',l.strictEqual)};l.notStrictEqual=function(a,b,c){a===b&&r(a,b,c,'!==',l.notStrictEqual)};function x(a,b){if(!a||!b){return!1}if(Object.prototype.toString.call(b)=='[object RegExp]'){return b.test(a)}try{if(a instanceof b){return!0}}catch(a){};if(Error.isPrototypeOf(b)){return!1}return b.call({},a)===!0}function y(a){var b;try{a()}catch(a){b=a};return b}function z(a,b,c,d){var e;if(typeof b!=='function'){throw new TypeError('"block" argument must be a function')}typeof c==='string'&&(d=c,c=null);e=y(b);d=(c&&c.name?' ('+c.name+').':'.')+(d?' '+d:'.');a&&!e&&r(e,c,'Missing expected exception'+d);var g=typeof d==='string',h=!a&&f.isError(e),i=!a&&e&&!c;(h&&g&&x(e,c)||i)&&r(e,c,'Got unwanted exception'+d);if(a&&e&&c&&!x(e,c)||!a&&e){throw e}}l.throws=function(a,b,c){z(!0,a,b,c)};l.doesNotThrow=function(a,b,c){z(!1,a,b,c)};l.ifError=function(a){if(a)throw a};var A=Object.keys||function(a){var b=[];for(var c in a)g.call(a,c)&&b.push(c);return b}}).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{"util/":7}],3:[function(a,b,c){var d=b.exports={},e,f;function g(){throw new Error('setTimeout has not been defined')}function h(){throw new Error('clearTimeout has not been defined')}!function(){try{typeof setTimeout==='function'?(e=setTimeout):(e=g)}catch(a){e=g};try{typeof clearTimeout==='function'?(f=clearTimeout):(f=h)}catch(a){f=h}}();function i(a){if(e===setTimeout){return setTimeout(a,0)}if((e===g||!e)&&setTimeout){e=setTimeout;return setTimeout(a,0)}try{return e(a,0)}catch(b){try{return e.call(null,a,0)}catch(b){return e.call(this,a,0)}}}function j(a){if(f===clearTimeout){return clearTimeout(a)}if((f===h||!f)&&clearTimeout){f=clearTimeout;return clearTimeout(a)}try{return f(a)}catch(b){try{return f.call(null,a)}catch(b){return f.call(this,a)}}}var k=[],l=!1,m,n=-1;function o(){if(!l||!m){return}l=!1;m.length?(k=m.concat(k)):(n=-1);k.length&&p()}function p(){if(l){return}var a=i(o);l=!0;var b=k.length;while(b){m=k;k=[];while(++n1){for(var c=1;cf){if(d){throw new Error("too few values provided for key `"+b+"`")}else{return}}g=Array.isArray(h)?h[e]:h;d&&(c[b]=e+1);return g};c=function(a,b,d){var e,f;if(Array.isArray(a)){e=-1;f=a.length;while(++e=f)return a;switch(a){case '%s':return String(d[c++]);case '%d':return Number(d[c++]);case '%j':try{return JSON.stringify(d[c++])}catch(a){return'[Circular]'};default:return a}});for(var i=d[c];c=3&&(d.depth=arguments[2]);arguments.length>=4&&(d.colors=arguments[3]);s(b)?(d.showHidden=b):b&&c._extend(d,b);y(d.showHidden)&&(d.showHidden=!1);y(d.depth)&&(d.depth=2);y(d.colors)&&(d.colors=!1);y(d.customInspect)&&(d.customInspect=!0);d.colors&&(d.stylize=i);return l(d,a,d.depth)}c.inspect=h;h.colors={'bold':[1,22],'italic':[3,23],'underline':[4,24],'inverse':[7,27],'white':[37,39],'grey':[90,39],'black':[30,39],'blue':[34,39],'cyan':[36,39],'green':[32,39],'magenta':[35,39],'red':[31,39],'yellow':[33,39]};h.styles={'special':'cyan','number':'yellow','boolean':'yellow','undefined':'grey','null':'bold','string':'green','date':'magenta','regexp':'red'};function i(a,b){var c=h.styles[b];if(c){return'\u001b['+h.colors[c][0]+'m'+a+'\u001b['+h.colors[c][1]+'m'}else{return a}}function j(a,b){return a}function k(a){var b={};a.forEach(function(a,c){b[a]=!0});return b}function l(a,b,d){if(a.customInspect&&b&&D(b.inspect)&&b.inspect!==c.inspect&&!(b.constructor&&b.constructor.prototype===b)){var e=b.inspect(d,a);w(e)||(e=l(a,e,d));return e}var f=m(a,b);if(f){return f}var g=Object.keys(b),h=k(g);a.showHidden&&(g=Object.getOwnPropertyNames(b));if(C(b)&&(g.indexOf('message')>=0||g.indexOf('description')>=0)){return n(b)}if(g.length===0){if(D(b)){var i=b.name?': '+b.name:'';return a.stylize('[Function'+i+']','special')}if(z(b)){return a.stylize(RegExp.prototype.toString.call(b),'regexp')}if(B(b)){return a.stylize(Date.prototype.toString.call(b),'date')}if(C(b)){return n(b)}}var j='',s=!1,t=['{','}'];r(b)&&(s=!0,t=['[',']']);if(D(b)){var u=b.name?': '+b.name:'';j=' [Function'+u+']'}z(b)&&(j=' '+RegExp.prototype.toString.call(b));B(b)&&(j=' '+Date.prototype.toUTCString.call(b));C(b)&&(j=' '+n(b));if(g.length===0&&(!s||b.length==0)){return t[0]+j+t[1]}if(d<0){if(z(b)){return a.stylize(RegExp.prototype.toString.call(b),'regexp')}else{return a.stylize('[Object]','special')}}a.seen.push(b);var v;s?(v=o(a,b,d,h,g)):(v=g.map(function(c){return p(a,b,d,h,c,s)}));a.seen.pop();return q(v,j,t)}function m(a,b){if(y(b))return a.stylize('undefined','undefined');if(w(b)){var c='\''+JSON.stringify(b).replace(/^"|"$/g,'').replace(/'/g,"\\'").replace(/\\"/g,'"')+'\'';return a.stylize(c,'string')}if(v(b))return a.stylize(''+b,'number');if(s(b))return a.stylize(''+b,'boolean');if(t(b))return a.stylize('null','null')}function n(a){return'['+Error.prototype.toString.call(a)+']'}function o(a,b,c,d,e){var f=[];for(var g=0,h=b.length;g-1&&(f?(h=h.split('\n').map(function(a){return' '+a}).join('\n').substr(2)):(h='\n'+h.split('\n').map(function(a){return' '+a}).join('\n')))):(h=a.stylize('[Circular]','special')));if(y(g)){if(f&&e.match(/^\d+$/)){return h}g=JSON.stringify(''+e);g.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?(g=g.substr(1,g.length-2),g=a.stylize(g,'name')):(g=g.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'"),g=a.stylize(g,'string'))}return g+': '+h}function q(a,b,c){var d=0,e=a.reduce(function(a,b){d++;b.indexOf('\n')>=0&&d++;return a+b.replace(/\u001b\[\d\d?m/g,'').length+1},0);if(e>60){return c[0]+(b===''?'':b+'\n ')+' '+a.join(',\n ')+' '+c[1]}return c[0]+b+' '+a.join(', ')+' '+c[1]}function r(a){return Array.isArray(a)}c.isArray=r;function s(a){return typeof a==='boolean'}c.isBoolean=s;function t(a){return a===null}c.isNull=t;function u(a){return a==null}c.isNullOrUndefined=u;function v(a){return typeof a==='number'}c.isNumber=v;function w(a){return typeof a==='string'}c.isString=w;function x(a){return typeof a==='symbol'}c.isSymbol=x;function y(a){return a===void 0}c.isUndefined=y;function z(a){return A(a)&&F(a)==='[object RegExp]'}c.isRegExp=z;function A(a){return typeof a==='object'&&a!==null}c.isObject=A;function B(a){return A(a)&&F(a)==='[object Date]'}c.isDate=B;function C(a){return A(a)&&(F(a)==='[object Error]'||a instanceof Error)}c.isError=C;function D(a){return typeof a==='function'}c.isFunction=D;function E(a){return a===null||typeof a==='boolean'||typeof a==='number'||typeof a==='string'||typeof a==='symbol'||typeof a==='undefined'}c.isPrimitive=E;c.isBuffer=a('./support/isBuffer');function F(a){return Object.prototype.toString.call(a)}function G(a){return a<10?'0'+a.toString(10):a.toString(10)}var H=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];function I(){var a=new Date,b=[G(a.getHours()),G(a.getMinutes()),G(a.getSeconds())].join(':');return[a.getDate(),H[a.getMonth()],b].join(' ')}c.log=function(){console.log('%s - %s',I(),c.format.apply(c,arguments))};c.inherits=a('inherits');c._extend=function(a,b){if(!b||!A(b))return a;var c=Object.keys(b),d=c.length;while(d--)a[c[d]]=b[c[d]];return a};function J(a,b){return Object.prototype.hasOwnProperty.call(a,b)}}).call(this,a('_process'),typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{"./support/isBuffer":6,"_process":3,"inherits":5}]},{},[1])(1)}) 2 | -------------------------------------------------------------------------------- /dist/bundle.web.js: -------------------------------------------------------------------------------- 1 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.RouterSingleton = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) 71 | assert.notEqual(this.root, null) 72 | this.rootEl = document.querySelector(selector) || document.body 73 | this.requestStateUpdate() 74 | } 75 | 76 | Router.prototype.onPopState = function onPopState (e) { 77 | if (e) e.preventDefault() 78 | this.requestStateUpdate(e) 79 | } 80 | 81 | Router.prototype.getRoute = function getRoute (path) { 82 | assert.equal(typeof path, 'string') 83 | var posibleRoutes = [] 84 | for (var pattern in this.routes) { 85 | if (this.routes.hasOwnProperty(pattern)) { 86 | if (this.routes[pattern]._urlPattern.match(path) !== null) posibleRoutes.push(this.routes[pattern]) 87 | } 88 | } 89 | // there are more than one candidate 90 | if (posibleRoutes.length === 0) return null 91 | return posibleRoutes[0] 92 | } 93 | 94 | Router.prototype.notFound = function notFound (notFoundView) { 95 | assert.equal(typeof notFoundView, 'function') 96 | this.default = new Route(null, notFoundView) 97 | } 98 | 99 | Router.prototype.goToPath = function goToPath (path, title) { 100 | title = title || null 101 | // Only process real changes. 102 | if (path === window.location.pathname) { 103 | return 104 | } 105 | var self = this 106 | this.previousPath = window.location.pathname 107 | this.currentPath = path 108 | this.previousRoute = this.currentRoute || this.root 109 | this.currentRoute = this.getRoute(this.currentPath) 110 | // assert(this.currentRoute) 111 | 112 | window.history.pushState(undefined, title, path) 113 | window.requestAnimationFrame(function () { 114 | self.manageState() 115 | }) 116 | } 117 | 118 | Router.prototype.manageState = function manageState () { 119 | var self = this 120 | if (this.currentPath === this.previousPath) return 121 | if (!this.currentRoute && this.default) this.currentRoute = this.default 122 | // currentView is the new view to be added 123 | var currentView = this.currentRoute.onStart(this.store) 124 | var child = null 125 | 126 | // if (!this.rootEl.hasChildNodes(currentView)) { 127 | if (this.firstRender && currentView) { 128 | this.firstRender = false 129 | this.rootEl.appendChild(currentView) 130 | } else if (!this.onRender || typeof this.onRender !== 'function') { 131 | child = this.rootEl.lasttElementChild || this.rootEl.lastChild 132 | this.rootEl.replaceChild(currentView, child) 133 | } else { 134 | child = this.rootEl.lasttElementChild || this.rootEl.lastChild 135 | this.onRender(currentView, child, this.currentRoute.cb) 136 | } 137 | var links = document.querySelectorAll('a[data-route]') 138 | Array.prototype.forEach.call(links, function (link) { 139 | link.addEventListener('click', function (e) { 140 | e.preventDefault() 141 | if (!link.getAttribute('data-bind')) self.goToPath(link.getAttribute('data-route')) 142 | if (typeof self.onNavClick === 'function') self.onNavClick(link.getAttribute('data-route'), link) 143 | }) 144 | }) 145 | if ((!this.onRender || typeof this.onRender !== 'function') && typeof this.currentRoute.cb === 'function') { 146 | try { 147 | this.currentRoute.cb() 148 | } catch (ex) { 149 | console.error(ex) 150 | } 151 | } 152 | } 153 | 154 | Router.prototype.requestStateUpdate = function requestStateUpdate (e) { 155 | this.previousPath = this.currentPath 156 | this.currentPath = window.location.pathname 157 | this.currentRoute = this.getRoute(e && e.target !== window 158 | ? e.target.getAttribute('data-route') 159 | : window.location.pathname) 160 | var self = this 161 | window.requestAnimationFrame(function () { 162 | self.manageState() 163 | }) 164 | } 165 | 166 | function Route (pattern, view, cb) { 167 | this.pattern = pattern 168 | this.cb = cb 169 | this._urlPattern = pattern ? new UrlPattern(pattern) : null 170 | this.view = view 171 | this.params = null 172 | this.path = null 173 | } 174 | 175 | Route.prototype.getParams = function getParams () { 176 | if (!this.path) return false 177 | return this.params 178 | } 179 | Route.prototype.setParams = function setParams () { 180 | if (!this.path) return false 181 | this.params = this._urlPattern ? this._urlPattern.match(this.path) : null 182 | } 183 | 184 | Route.prototype.onStart = function onStart (store) { 185 | this.path = window.location.pathname 186 | this.params = this._urlPattern ? this._urlPattern.match(this.path) : null 187 | return this.view(this.params, store) 188 | } 189 | 190 | module.exports = getRouter 191 | 192 | },{"assert":2,"url-pattern":4}],2:[function(require,module,exports){ 193 | (function (global){ 194 | 'use strict'; 195 | 196 | // compare and isBuffer taken from https://github.com/feross/buffer/blob/680e9e5e488f22aac27599a57dc844a6315928dd/index.js 197 | // original notice: 198 | 199 | /*! 200 | * The buffer module from node.js, for the browser. 201 | * 202 | * @author Feross Aboukhadijeh 203 | * @license MIT 204 | */ 205 | function compare(a, b) { 206 | if (a === b) { 207 | return 0; 208 | } 209 | 210 | var x = a.length; 211 | var y = b.length; 212 | 213 | for (var i = 0, len = Math.min(x, y); i < len; ++i) { 214 | if (a[i] !== b[i]) { 215 | x = a[i]; 216 | y = b[i]; 217 | break; 218 | } 219 | } 220 | 221 | if (x < y) { 222 | return -1; 223 | } 224 | if (y < x) { 225 | return 1; 226 | } 227 | return 0; 228 | } 229 | function isBuffer(b) { 230 | if (global.Buffer && typeof global.Buffer.isBuffer === 'function') { 231 | return global.Buffer.isBuffer(b); 232 | } 233 | return !!(b != null && b._isBuffer); 234 | } 235 | 236 | // based on node assert, original notice: 237 | 238 | // http://wiki.commonjs.org/wiki/Unit_Testing/1.0 239 | // 240 | // THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! 241 | // 242 | // Originally from narwhal.js (http://narwhaljs.org) 243 | // Copyright (c) 2009 Thomas Robinson <280north.com> 244 | // 245 | // Permission is hereby granted, free of charge, to any person obtaining a copy 246 | // of this software and associated documentation files (the 'Software'), to 247 | // deal in the Software without restriction, including without limitation the 248 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 249 | // sell copies of the Software, and to permit persons to whom the Software is 250 | // furnished to do so, subject to the following conditions: 251 | // 252 | // The above copyright notice and this permission notice shall be included in 253 | // all copies or substantial portions of the Software. 254 | // 255 | // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 256 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 257 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 258 | // AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 259 | // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 260 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 261 | 262 | var util = require('util/'); 263 | var hasOwn = Object.prototype.hasOwnProperty; 264 | var pSlice = Array.prototype.slice; 265 | var functionsHaveNames = (function () { 266 | return function foo() {}.name === 'foo'; 267 | }()); 268 | function pToString (obj) { 269 | return Object.prototype.toString.call(obj); 270 | } 271 | function isView(arrbuf) { 272 | if (isBuffer(arrbuf)) { 273 | return false; 274 | } 275 | if (typeof global.ArrayBuffer !== 'function') { 276 | return false; 277 | } 278 | if (typeof ArrayBuffer.isView === 'function') { 279 | return ArrayBuffer.isView(arrbuf); 280 | } 281 | if (!arrbuf) { 282 | return false; 283 | } 284 | if (arrbuf instanceof DataView) { 285 | return true; 286 | } 287 | if (arrbuf.buffer && arrbuf.buffer instanceof ArrayBuffer) { 288 | return true; 289 | } 290 | return false; 291 | } 292 | // 1. The assert module provides functions that throw 293 | // AssertionError's when particular conditions are not met. The 294 | // assert module must conform to the following interface. 295 | 296 | var assert = module.exports = ok; 297 | 298 | // 2. The AssertionError is defined in assert. 299 | // new assert.AssertionError({ message: message, 300 | // actual: actual, 301 | // expected: expected }) 302 | 303 | var regex = /\s*function\s+([^\(\s]*)\s*/; 304 | // based on https://github.com/ljharb/function.prototype.name/blob/adeeeec8bfcc6068b187d7d9fb3d5bb1d3a30899/implementation.js 305 | function getName(func) { 306 | if (!util.isFunction(func)) { 307 | return; 308 | } 309 | if (functionsHaveNames) { 310 | return func.name; 311 | } 312 | var str = func.toString(); 313 | var match = str.match(regex); 314 | return match && match[1]; 315 | } 316 | assert.AssertionError = function AssertionError(options) { 317 | this.name = 'AssertionError'; 318 | this.actual = options.actual; 319 | this.expected = options.expected; 320 | this.operator = options.operator; 321 | if (options.message) { 322 | this.message = options.message; 323 | this.generatedMessage = false; 324 | } else { 325 | this.message = getMessage(this); 326 | this.generatedMessage = true; 327 | } 328 | var stackStartFunction = options.stackStartFunction || fail; 329 | if (Error.captureStackTrace) { 330 | Error.captureStackTrace(this, stackStartFunction); 331 | } else { 332 | // non v8 browsers so we can have a stacktrace 333 | var err = new Error(); 334 | if (err.stack) { 335 | var out = err.stack; 336 | 337 | // try to strip useless frames 338 | var fn_name = getName(stackStartFunction); 339 | var idx = out.indexOf('\n' + fn_name); 340 | if (idx >= 0) { 341 | // once we have located the function frame 342 | // we need to strip out everything before it (and its line) 343 | var next_line = out.indexOf('\n', idx + 1); 344 | out = out.substring(next_line + 1); 345 | } 346 | 347 | this.stack = out; 348 | } 349 | } 350 | }; 351 | 352 | // assert.AssertionError instanceof Error 353 | util.inherits(assert.AssertionError, Error); 354 | 355 | function truncate(s, n) { 356 | if (typeof s === 'string') { 357 | return s.length < n ? s : s.slice(0, n); 358 | } else { 359 | return s; 360 | } 361 | } 362 | function inspect(something) { 363 | if (functionsHaveNames || !util.isFunction(something)) { 364 | return util.inspect(something); 365 | } 366 | var rawname = getName(something); 367 | var name = rawname ? ': ' + rawname : ''; 368 | return '[Function' + name + ']'; 369 | } 370 | function getMessage(self) { 371 | return truncate(inspect(self.actual), 128) + ' ' + 372 | self.operator + ' ' + 373 | truncate(inspect(self.expected), 128); 374 | } 375 | 376 | // At present only the three keys mentioned above are used and 377 | // understood by the spec. Implementations or sub modules can pass 378 | // other keys to the AssertionError's constructor - they will be 379 | // ignored. 380 | 381 | // 3. All of the following functions must throw an AssertionError 382 | // when a corresponding condition is not met, with a message that 383 | // may be undefined if not provided. All assertion methods provide 384 | // both the actual and expected values to the assertion error for 385 | // display purposes. 386 | 387 | function fail(actual, expected, message, operator, stackStartFunction) { 388 | throw new assert.AssertionError({ 389 | message: message, 390 | actual: actual, 391 | expected: expected, 392 | operator: operator, 393 | stackStartFunction: stackStartFunction 394 | }); 395 | } 396 | 397 | // EXTENSION! allows for well behaved errors defined elsewhere. 398 | assert.fail = fail; 399 | 400 | // 4. Pure assertion tests whether a value is truthy, as determined 401 | // by !!guard. 402 | // assert.ok(guard, message_opt); 403 | // This statement is equivalent to assert.equal(true, !!guard, 404 | // message_opt);. To test strictly for the value true, use 405 | // assert.strictEqual(true, guard, message_opt);. 406 | 407 | function ok(value, message) { 408 | if (!value) fail(value, true, message, '==', assert.ok); 409 | } 410 | assert.ok = ok; 411 | 412 | // 5. The equality assertion tests shallow, coercive equality with 413 | // ==. 414 | // assert.equal(actual, expected, message_opt); 415 | 416 | assert.equal = function equal(actual, expected, message) { 417 | if (actual != expected) fail(actual, expected, message, '==', assert.equal); 418 | }; 419 | 420 | // 6. The non-equality assertion tests for whether two objects are not equal 421 | // with != assert.notEqual(actual, expected, message_opt); 422 | 423 | assert.notEqual = function notEqual(actual, expected, message) { 424 | if (actual == expected) { 425 | fail(actual, expected, message, '!=', assert.notEqual); 426 | } 427 | }; 428 | 429 | // 7. The equivalence assertion tests a deep equality relation. 430 | // assert.deepEqual(actual, expected, message_opt); 431 | 432 | assert.deepEqual = function deepEqual(actual, expected, message) { 433 | if (!_deepEqual(actual, expected, false)) { 434 | fail(actual, expected, message, 'deepEqual', assert.deepEqual); 435 | } 436 | }; 437 | 438 | assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) { 439 | if (!_deepEqual(actual, expected, true)) { 440 | fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual); 441 | } 442 | }; 443 | 444 | function _deepEqual(actual, expected, strict, memos) { 445 | // 7.1. All identical values are equivalent, as determined by ===. 446 | if (actual === expected) { 447 | return true; 448 | } else if (isBuffer(actual) && isBuffer(expected)) { 449 | return compare(actual, expected) === 0; 450 | 451 | // 7.2. If the expected value is a Date object, the actual value is 452 | // equivalent if it is also a Date object that refers to the same time. 453 | } else if (util.isDate(actual) && util.isDate(expected)) { 454 | return actual.getTime() === expected.getTime(); 455 | 456 | // 7.3 If the expected value is a RegExp object, the actual value is 457 | // equivalent if it is also a RegExp object with the same source and 458 | // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). 459 | } else if (util.isRegExp(actual) && util.isRegExp(expected)) { 460 | return actual.source === expected.source && 461 | actual.global === expected.global && 462 | actual.multiline === expected.multiline && 463 | actual.lastIndex === expected.lastIndex && 464 | actual.ignoreCase === expected.ignoreCase; 465 | 466 | // 7.4. Other pairs that do not both pass typeof value == 'object', 467 | // equivalence is determined by ==. 468 | } else if ((actual === null || typeof actual !== 'object') && 469 | (expected === null || typeof expected !== 'object')) { 470 | return strict ? actual === expected : actual == expected; 471 | 472 | // If both values are instances of typed arrays, wrap their underlying 473 | // ArrayBuffers in a Buffer each to increase performance 474 | // This optimization requires the arrays to have the same type as checked by 475 | // Object.prototype.toString (aka pToString). Never perform binary 476 | // comparisons for Float*Arrays, though, since e.g. +0 === -0 but their 477 | // bit patterns are not identical. 478 | } else if (isView(actual) && isView(expected) && 479 | pToString(actual) === pToString(expected) && 480 | !(actual instanceof Float32Array || 481 | actual instanceof Float64Array)) { 482 | return compare(new Uint8Array(actual.buffer), 483 | new Uint8Array(expected.buffer)) === 0; 484 | 485 | // 7.5 For all other Object pairs, including Array objects, equivalence is 486 | // determined by having the same number of owned properties (as verified 487 | // with Object.prototype.hasOwnProperty.call), the same set of keys 488 | // (although not necessarily the same order), equivalent values for every 489 | // corresponding key, and an identical 'prototype' property. Note: this 490 | // accounts for both named and indexed properties on Arrays. 491 | } else if (isBuffer(actual) !== isBuffer(expected)) { 492 | return false; 493 | } else { 494 | memos = memos || {actual: [], expected: []}; 495 | 496 | var actualIndex = memos.actual.indexOf(actual); 497 | if (actualIndex !== -1) { 498 | if (actualIndex === memos.expected.indexOf(expected)) { 499 | return true; 500 | } 501 | } 502 | 503 | memos.actual.push(actual); 504 | memos.expected.push(expected); 505 | 506 | return objEquiv(actual, expected, strict, memos); 507 | } 508 | } 509 | 510 | function isArguments(object) { 511 | return Object.prototype.toString.call(object) == '[object Arguments]'; 512 | } 513 | 514 | function objEquiv(a, b, strict, actualVisitedObjects) { 515 | if (a === null || a === undefined || b === null || b === undefined) 516 | return false; 517 | // if one is a primitive, the other must be same 518 | if (util.isPrimitive(a) || util.isPrimitive(b)) 519 | return a === b; 520 | if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) 521 | return false; 522 | var aIsArgs = isArguments(a); 523 | var bIsArgs = isArguments(b); 524 | if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) 525 | return false; 526 | if (aIsArgs) { 527 | a = pSlice.call(a); 528 | b = pSlice.call(b); 529 | return _deepEqual(a, b, strict); 530 | } 531 | var ka = objectKeys(a); 532 | var kb = objectKeys(b); 533 | var key, i; 534 | // having the same number of owned properties (keys incorporates 535 | // hasOwnProperty) 536 | if (ka.length !== kb.length) 537 | return false; 538 | //the same set of keys (although not necessarily the same order), 539 | ka.sort(); 540 | kb.sort(); 541 | //~~~cheap key test 542 | for (i = ka.length - 1; i >= 0; i--) { 543 | if (ka[i] !== kb[i]) 544 | return false; 545 | } 546 | //equivalent values for every corresponding key, and 547 | //~~~possibly expensive deep test 548 | for (i = ka.length - 1; i >= 0; i--) { 549 | key = ka[i]; 550 | if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects)) 551 | return false; 552 | } 553 | return true; 554 | } 555 | 556 | // 8. The non-equivalence assertion tests for any deep inequality. 557 | // assert.notDeepEqual(actual, expected, message_opt); 558 | 559 | assert.notDeepEqual = function notDeepEqual(actual, expected, message) { 560 | if (_deepEqual(actual, expected, false)) { 561 | fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); 562 | } 563 | }; 564 | 565 | assert.notDeepStrictEqual = notDeepStrictEqual; 566 | function notDeepStrictEqual(actual, expected, message) { 567 | if (_deepEqual(actual, expected, true)) { 568 | fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual); 569 | } 570 | } 571 | 572 | 573 | // 9. The strict equality assertion tests strict equality, as determined by ===. 574 | // assert.strictEqual(actual, expected, message_opt); 575 | 576 | assert.strictEqual = function strictEqual(actual, expected, message) { 577 | if (actual !== expected) { 578 | fail(actual, expected, message, '===', assert.strictEqual); 579 | } 580 | }; 581 | 582 | // 10. The strict non-equality assertion tests for strict inequality, as 583 | // determined by !==. assert.notStrictEqual(actual, expected, message_opt); 584 | 585 | assert.notStrictEqual = function notStrictEqual(actual, expected, message) { 586 | if (actual === expected) { 587 | fail(actual, expected, message, '!==', assert.notStrictEqual); 588 | } 589 | }; 590 | 591 | function expectedException(actual, expected) { 592 | if (!actual || !expected) { 593 | return false; 594 | } 595 | 596 | if (Object.prototype.toString.call(expected) == '[object RegExp]') { 597 | return expected.test(actual); 598 | } 599 | 600 | try { 601 | if (actual instanceof expected) { 602 | return true; 603 | } 604 | } catch (e) { 605 | // Ignore. The instanceof check doesn't work for arrow functions. 606 | } 607 | 608 | if (Error.isPrototypeOf(expected)) { 609 | return false; 610 | } 611 | 612 | return expected.call({}, actual) === true; 613 | } 614 | 615 | function _tryBlock(block) { 616 | var error; 617 | try { 618 | block(); 619 | } catch (e) { 620 | error = e; 621 | } 622 | return error; 623 | } 624 | 625 | function _throws(shouldThrow, block, expected, message) { 626 | var actual; 627 | 628 | if (typeof block !== 'function') { 629 | throw new TypeError('"block" argument must be a function'); 630 | } 631 | 632 | if (typeof expected === 'string') { 633 | message = expected; 634 | expected = null; 635 | } 636 | 637 | actual = _tryBlock(block); 638 | 639 | message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + 640 | (message ? ' ' + message : '.'); 641 | 642 | if (shouldThrow && !actual) { 643 | fail(actual, expected, 'Missing expected exception' + message); 644 | } 645 | 646 | var userProvidedMessage = typeof message === 'string'; 647 | var isUnwantedException = !shouldThrow && util.isError(actual); 648 | var isUnexpectedException = !shouldThrow && actual && !expected; 649 | 650 | if ((isUnwantedException && 651 | userProvidedMessage && 652 | expectedException(actual, expected)) || 653 | isUnexpectedException) { 654 | fail(actual, expected, 'Got unwanted exception' + message); 655 | } 656 | 657 | if ((shouldThrow && actual && expected && 658 | !expectedException(actual, expected)) || (!shouldThrow && actual)) { 659 | throw actual; 660 | } 661 | } 662 | 663 | // 11. Expected to throw an error: 664 | // assert.throws(block, Error_opt, message_opt); 665 | 666 | assert.throws = function(block, /*optional*/error, /*optional*/message) { 667 | _throws(true, block, error, message); 668 | }; 669 | 670 | // EXTENSION! This is annoying to write outside this module. 671 | assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { 672 | _throws(false, block, error, message); 673 | }; 674 | 675 | assert.ifError = function(err) { if (err) throw err; }; 676 | 677 | var objectKeys = Object.keys || function (obj) { 678 | var keys = []; 679 | for (var key in obj) { 680 | if (hasOwn.call(obj, key)) keys.push(key); 681 | } 682 | return keys; 683 | }; 684 | 685 | }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 686 | },{"util/":7}],3:[function(require,module,exports){ 687 | // shim for using process in browser 688 | var process = module.exports = {}; 689 | 690 | // cached from whatever global is present so that test runners that stub it 691 | // don't break things. But we need to wrap it in a try catch in case it is 692 | // wrapped in strict mode code which doesn't define any globals. It's inside a 693 | // function because try/catches deoptimize in certain engines. 694 | 695 | var cachedSetTimeout; 696 | var cachedClearTimeout; 697 | 698 | function defaultSetTimout() { 699 | throw new Error('setTimeout has not been defined'); 700 | } 701 | function defaultClearTimeout () { 702 | throw new Error('clearTimeout has not been defined'); 703 | } 704 | (function () { 705 | try { 706 | if (typeof setTimeout === 'function') { 707 | cachedSetTimeout = setTimeout; 708 | } else { 709 | cachedSetTimeout = defaultSetTimout; 710 | } 711 | } catch (e) { 712 | cachedSetTimeout = defaultSetTimout; 713 | } 714 | try { 715 | if (typeof clearTimeout === 'function') { 716 | cachedClearTimeout = clearTimeout; 717 | } else { 718 | cachedClearTimeout = defaultClearTimeout; 719 | } 720 | } catch (e) { 721 | cachedClearTimeout = defaultClearTimeout; 722 | } 723 | } ()) 724 | function runTimeout(fun) { 725 | if (cachedSetTimeout === setTimeout) { 726 | //normal enviroments in sane situations 727 | return setTimeout(fun, 0); 728 | } 729 | // if setTimeout wasn't available but was latter defined 730 | if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { 731 | cachedSetTimeout = setTimeout; 732 | return setTimeout(fun, 0); 733 | } 734 | try { 735 | // when when somebody has screwed with setTimeout but no I.E. maddness 736 | return cachedSetTimeout(fun, 0); 737 | } catch(e){ 738 | try { 739 | // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally 740 | return cachedSetTimeout.call(null, fun, 0); 741 | } catch(e){ 742 | // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error 743 | return cachedSetTimeout.call(this, fun, 0); 744 | } 745 | } 746 | 747 | 748 | } 749 | function runClearTimeout(marker) { 750 | if (cachedClearTimeout === clearTimeout) { 751 | //normal enviroments in sane situations 752 | return clearTimeout(marker); 753 | } 754 | // if clearTimeout wasn't available but was latter defined 755 | if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { 756 | cachedClearTimeout = clearTimeout; 757 | return clearTimeout(marker); 758 | } 759 | try { 760 | // when when somebody has screwed with setTimeout but no I.E. maddness 761 | return cachedClearTimeout(marker); 762 | } catch (e){ 763 | try { 764 | // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally 765 | return cachedClearTimeout.call(null, marker); 766 | } catch (e){ 767 | // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. 768 | // Some versions of I.E. have different rules for clearTimeout vs setTimeout 769 | return cachedClearTimeout.call(this, marker); 770 | } 771 | } 772 | 773 | 774 | 775 | } 776 | var queue = []; 777 | var draining = false; 778 | var currentQueue; 779 | var queueIndex = -1; 780 | 781 | function cleanUpNextTick() { 782 | if (!draining || !currentQueue) { 783 | return; 784 | } 785 | draining = false; 786 | if (currentQueue.length) { 787 | queue = currentQueue.concat(queue); 788 | } else { 789 | queueIndex = -1; 790 | } 791 | if (queue.length) { 792 | drainQueue(); 793 | } 794 | } 795 | 796 | function drainQueue() { 797 | if (draining) { 798 | return; 799 | } 800 | var timeout = runTimeout(cleanUpNextTick); 801 | draining = true; 802 | 803 | var len = queue.length; 804 | while(len) { 805 | currentQueue = queue; 806 | queue = []; 807 | while (++queueIndex < len) { 808 | if (currentQueue) { 809 | currentQueue[queueIndex].run(); 810 | } 811 | } 812 | queueIndex = -1; 813 | len = queue.length; 814 | } 815 | currentQueue = null; 816 | draining = false; 817 | runClearTimeout(timeout); 818 | } 819 | 820 | process.nextTick = function (fun) { 821 | var args = new Array(arguments.length - 1); 822 | if (arguments.length > 1) { 823 | for (var i = 1; i < arguments.length; i++) { 824 | args[i - 1] = arguments[i]; 825 | } 826 | } 827 | queue.push(new Item(fun, args)); 828 | if (queue.length === 1 && !draining) { 829 | runTimeout(drainQueue); 830 | } 831 | }; 832 | 833 | // v8 likes predictible objects 834 | function Item(fun, array) { 835 | this.fun = fun; 836 | this.array = array; 837 | } 838 | Item.prototype.run = function () { 839 | this.fun.apply(null, this.array); 840 | }; 841 | process.title = 'browser'; 842 | process.browser = true; 843 | process.env = {}; 844 | process.argv = []; 845 | process.version = ''; // empty string to avoid regexp issues 846 | process.versions = {}; 847 | 848 | function noop() {} 849 | 850 | process.on = noop; 851 | process.addListener = noop; 852 | process.once = noop; 853 | process.off = noop; 854 | process.removeListener = noop; 855 | process.removeAllListeners = noop; 856 | process.emit = noop; 857 | process.prependListener = noop; 858 | process.prependOnceListener = noop; 859 | 860 | process.listeners = function (name) { return [] } 861 | 862 | process.binding = function (name) { 863 | throw new Error('process.binding is not supported'); 864 | }; 865 | 866 | process.cwd = function () { return '/' }; 867 | process.chdir = function (dir) { 868 | throw new Error('process.chdir is not supported'); 869 | }; 870 | process.umask = function() { return 0; }; 871 | 872 | },{}],4:[function(require,module,exports){ 873 | // Generated by CoffeeScript 1.10.0 874 | var slice = [].slice; 875 | 876 | (function(root, factory) { 877 | if (('function' === typeof define) && (define.amd != null)) { 878 | return define([], factory); 879 | } else if (typeof exports !== "undefined" && exports !== null) { 880 | return module.exports = factory(); 881 | } else { 882 | return root.UrlPattern = factory(); 883 | } 884 | })(this, function() { 885 | var P, UrlPattern, astNodeContainsSegmentsForProvidedParams, astNodeToNames, astNodeToRegexString, baseAstNodeToRegexString, concatMap, defaultOptions, escapeForRegex, getParam, keysAndValuesToObject, newParser, regexGroupCount, stringConcatMap, stringify; 886 | escapeForRegex = function(string) { 887 | return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); 888 | }; 889 | concatMap = function(array, f) { 890 | var i, length, results; 891 | results = []; 892 | i = -1; 893 | length = array.length; 894 | while (++i < length) { 895 | results = results.concat(f(array[i])); 896 | } 897 | return results; 898 | }; 899 | stringConcatMap = function(array, f) { 900 | var i, length, result; 901 | result = ''; 902 | i = -1; 903 | length = array.length; 904 | while (++i < length) { 905 | result += f(array[i]); 906 | } 907 | return result; 908 | }; 909 | regexGroupCount = function(regex) { 910 | return (new RegExp(regex.toString() + '|')).exec('').length - 1; 911 | }; 912 | keysAndValuesToObject = function(keys, values) { 913 | var i, key, length, object, value; 914 | object = {}; 915 | i = -1; 916 | length = keys.length; 917 | while (++i < length) { 918 | key = keys[i]; 919 | value = values[i]; 920 | if (value == null) { 921 | continue; 922 | } 923 | if (object[key] != null) { 924 | if (!Array.isArray(object[key])) { 925 | object[key] = [object[key]]; 926 | } 927 | object[key].push(value); 928 | } else { 929 | object[key] = value; 930 | } 931 | } 932 | return object; 933 | }; 934 | P = {}; 935 | P.Result = function(value, rest) { 936 | this.value = value; 937 | this.rest = rest; 938 | }; 939 | P.Tagged = function(tag, value) { 940 | this.tag = tag; 941 | this.value = value; 942 | }; 943 | P.tag = function(tag, parser) { 944 | return function(input) { 945 | var result, tagged; 946 | result = parser(input); 947 | if (result == null) { 948 | return; 949 | } 950 | tagged = new P.Tagged(tag, result.value); 951 | return new P.Result(tagged, result.rest); 952 | }; 953 | }; 954 | P.regex = function(regex) { 955 | return function(input) { 956 | var matches, result; 957 | matches = regex.exec(input); 958 | if (matches == null) { 959 | return; 960 | } 961 | result = matches[0]; 962 | return new P.Result(result, input.slice(result.length)); 963 | }; 964 | }; 965 | P.sequence = function() { 966 | var parsers; 967 | parsers = 1 <= arguments.length ? slice.call(arguments, 0) : []; 968 | return function(input) { 969 | var i, length, parser, rest, result, values; 970 | i = -1; 971 | length = parsers.length; 972 | values = []; 973 | rest = input; 974 | while (++i < length) { 975 | parser = parsers[i]; 976 | result = parser(rest); 977 | if (result == null) { 978 | return; 979 | } 980 | values.push(result.value); 981 | rest = result.rest; 982 | } 983 | return new P.Result(values, rest); 984 | }; 985 | }; 986 | P.pick = function() { 987 | var indexes, parsers; 988 | indexes = arguments[0], parsers = 2 <= arguments.length ? slice.call(arguments, 1) : []; 989 | return function(input) { 990 | var array, result; 991 | result = P.sequence.apply(P, parsers)(input); 992 | if (result == null) { 993 | return; 994 | } 995 | array = result.value; 996 | result.value = array[indexes]; 997 | return result; 998 | }; 999 | }; 1000 | P.string = function(string) { 1001 | var length; 1002 | length = string.length; 1003 | return function(input) { 1004 | if (input.slice(0, length) === string) { 1005 | return new P.Result(string, input.slice(length)); 1006 | } 1007 | }; 1008 | }; 1009 | P.lazy = function(fn) { 1010 | var cached; 1011 | cached = null; 1012 | return function(input) { 1013 | if (cached == null) { 1014 | cached = fn(); 1015 | } 1016 | return cached(input); 1017 | }; 1018 | }; 1019 | P.baseMany = function(parser, end, stringResult, atLeastOneResultRequired, input) { 1020 | var endResult, parserResult, rest, results; 1021 | rest = input; 1022 | results = stringResult ? '' : []; 1023 | while (true) { 1024 | if (end != null) { 1025 | endResult = end(rest); 1026 | if (endResult != null) { 1027 | break; 1028 | } 1029 | } 1030 | parserResult = parser(rest); 1031 | if (parserResult == null) { 1032 | break; 1033 | } 1034 | if (stringResult) { 1035 | results += parserResult.value; 1036 | } else { 1037 | results.push(parserResult.value); 1038 | } 1039 | rest = parserResult.rest; 1040 | } 1041 | if (atLeastOneResultRequired && results.length === 0) { 1042 | return; 1043 | } 1044 | return new P.Result(results, rest); 1045 | }; 1046 | P.many1 = function(parser) { 1047 | return function(input) { 1048 | return P.baseMany(parser, null, false, true, input); 1049 | }; 1050 | }; 1051 | P.concatMany1Till = function(parser, end) { 1052 | return function(input) { 1053 | return P.baseMany(parser, end, true, true, input); 1054 | }; 1055 | }; 1056 | P.firstChoice = function() { 1057 | var parsers; 1058 | parsers = 1 <= arguments.length ? slice.call(arguments, 0) : []; 1059 | return function(input) { 1060 | var i, length, parser, result; 1061 | i = -1; 1062 | length = parsers.length; 1063 | while (++i < length) { 1064 | parser = parsers[i]; 1065 | result = parser(input); 1066 | if (result != null) { 1067 | return result; 1068 | } 1069 | } 1070 | }; 1071 | }; 1072 | newParser = function(options) { 1073 | var U; 1074 | U = {}; 1075 | U.wildcard = P.tag('wildcard', P.string(options.wildcardChar)); 1076 | U.optional = P.tag('optional', P.pick(1, P.string(options.optionalSegmentStartChar), P.lazy(function() { 1077 | return U.pattern; 1078 | }), P.string(options.optionalSegmentEndChar))); 1079 | U.name = P.regex(new RegExp("^[" + options.segmentNameCharset + "]+")); 1080 | U.named = P.tag('named', P.pick(1, P.string(options.segmentNameStartChar), P.lazy(function() { 1081 | return U.name; 1082 | }))); 1083 | U.escapedChar = P.pick(1, P.string(options.escapeChar), P.regex(/^./)); 1084 | U["static"] = P.tag('static', P.concatMany1Till(P.firstChoice(P.lazy(function() { 1085 | return U.escapedChar; 1086 | }), P.regex(/^./)), P.firstChoice(P.string(options.segmentNameStartChar), P.string(options.optionalSegmentStartChar), P.string(options.optionalSegmentEndChar), U.wildcard))); 1087 | U.token = P.lazy(function() { 1088 | return P.firstChoice(U.wildcard, U.optional, U.named, U["static"]); 1089 | }); 1090 | U.pattern = P.many1(P.lazy(function() { 1091 | return U.token; 1092 | })); 1093 | return U; 1094 | }; 1095 | defaultOptions = { 1096 | escapeChar: '\\', 1097 | segmentNameStartChar: ':', 1098 | segmentValueCharset: 'a-zA-Z0-9-_~ %', 1099 | segmentNameCharset: 'a-zA-Z0-9', 1100 | optionalSegmentStartChar: '(', 1101 | optionalSegmentEndChar: ')', 1102 | wildcardChar: '*' 1103 | }; 1104 | baseAstNodeToRegexString = function(astNode, segmentValueCharset) { 1105 | if (Array.isArray(astNode)) { 1106 | return stringConcatMap(astNode, function(node) { 1107 | return baseAstNodeToRegexString(node, segmentValueCharset); 1108 | }); 1109 | } 1110 | switch (astNode.tag) { 1111 | case 'wildcard': 1112 | return '(.*?)'; 1113 | case 'named': 1114 | return "([" + segmentValueCharset + "]+)"; 1115 | case 'static': 1116 | return escapeForRegex(astNode.value); 1117 | case 'optional': 1118 | return '(?:' + baseAstNodeToRegexString(astNode.value, segmentValueCharset) + ')?'; 1119 | } 1120 | }; 1121 | astNodeToRegexString = function(astNode, segmentValueCharset) { 1122 | if (segmentValueCharset == null) { 1123 | segmentValueCharset = defaultOptions.segmentValueCharset; 1124 | } 1125 | return '^' + baseAstNodeToRegexString(astNode, segmentValueCharset) + '$'; 1126 | }; 1127 | astNodeToNames = function(astNode) { 1128 | if (Array.isArray(astNode)) { 1129 | return concatMap(astNode, astNodeToNames); 1130 | } 1131 | switch (astNode.tag) { 1132 | case 'wildcard': 1133 | return ['_']; 1134 | case 'named': 1135 | return [astNode.value]; 1136 | case 'static': 1137 | return []; 1138 | case 'optional': 1139 | return astNodeToNames(astNode.value); 1140 | } 1141 | }; 1142 | getParam = function(params, key, nextIndexes, sideEffects) { 1143 | var index, maxIndex, result, value; 1144 | if (sideEffects == null) { 1145 | sideEffects = false; 1146 | } 1147 | value = params[key]; 1148 | if (value == null) { 1149 | if (sideEffects) { 1150 | throw new Error("no values provided for key `" + key + "`"); 1151 | } else { 1152 | return; 1153 | } 1154 | } 1155 | index = nextIndexes[key] || 0; 1156 | maxIndex = Array.isArray(value) ? value.length - 1 : 0; 1157 | if (index > maxIndex) { 1158 | if (sideEffects) { 1159 | throw new Error("too few values provided for key `" + key + "`"); 1160 | } else { 1161 | return; 1162 | } 1163 | } 1164 | result = Array.isArray(value) ? value[index] : value; 1165 | if (sideEffects) { 1166 | nextIndexes[key] = index + 1; 1167 | } 1168 | return result; 1169 | }; 1170 | astNodeContainsSegmentsForProvidedParams = function(astNode, params, nextIndexes) { 1171 | var i, length; 1172 | if (Array.isArray(astNode)) { 1173 | i = -1; 1174 | length = astNode.length; 1175 | while (++i < length) { 1176 | if (astNodeContainsSegmentsForProvidedParams(astNode[i], params, nextIndexes)) { 1177 | return true; 1178 | } 1179 | } 1180 | return false; 1181 | } 1182 | switch (astNode.tag) { 1183 | case 'wildcard': 1184 | return getParam(params, '_', nextIndexes, false) != null; 1185 | case 'named': 1186 | return getParam(params, astNode.value, nextIndexes, false) != null; 1187 | case 'static': 1188 | return false; 1189 | case 'optional': 1190 | return astNodeContainsSegmentsForProvidedParams(astNode.value, params, nextIndexes); 1191 | } 1192 | }; 1193 | stringify = function(astNode, params, nextIndexes) { 1194 | if (Array.isArray(astNode)) { 1195 | return stringConcatMap(astNode, function(node) { 1196 | return stringify(node, params, nextIndexes); 1197 | }); 1198 | } 1199 | switch (astNode.tag) { 1200 | case 'wildcard': 1201 | return getParam(params, '_', nextIndexes, true); 1202 | case 'named': 1203 | return getParam(params, astNode.value, nextIndexes, true); 1204 | case 'static': 1205 | return astNode.value; 1206 | case 'optional': 1207 | if (astNodeContainsSegmentsForProvidedParams(astNode.value, params, nextIndexes)) { 1208 | return stringify(astNode.value, params, nextIndexes); 1209 | } else { 1210 | return ''; 1211 | } 1212 | } 1213 | }; 1214 | UrlPattern = function(arg1, arg2) { 1215 | var groupCount, options, parsed, parser, withoutWhitespace; 1216 | if (arg1 instanceof UrlPattern) { 1217 | this.isRegex = arg1.isRegex; 1218 | this.regex = arg1.regex; 1219 | this.ast = arg1.ast; 1220 | this.names = arg1.names; 1221 | return; 1222 | } 1223 | this.isRegex = arg1 instanceof RegExp; 1224 | if (!(('string' === typeof arg1) || this.isRegex)) { 1225 | throw new TypeError('argument must be a regex or a string'); 1226 | } 1227 | if (this.isRegex) { 1228 | this.regex = arg1; 1229 | if (arg2 != null) { 1230 | if (!Array.isArray(arg2)) { 1231 | throw new Error('if first argument is a regex the second argument may be an array of group names but you provided something else'); 1232 | } 1233 | groupCount = regexGroupCount(this.regex); 1234 | if (arg2.length !== groupCount) { 1235 | throw new Error("regex contains " + groupCount + " groups but array of group names contains " + arg2.length); 1236 | } 1237 | this.names = arg2; 1238 | } 1239 | return; 1240 | } 1241 | if (arg1 === '') { 1242 | throw new Error('argument must not be the empty string'); 1243 | } 1244 | withoutWhitespace = arg1.replace(/\s+/g, ''); 1245 | if (withoutWhitespace !== arg1) { 1246 | throw new Error('argument must not contain whitespace'); 1247 | } 1248 | options = { 1249 | escapeChar: (arg2 != null ? arg2.escapeChar : void 0) || defaultOptions.escapeChar, 1250 | segmentNameStartChar: (arg2 != null ? arg2.segmentNameStartChar : void 0) || defaultOptions.segmentNameStartChar, 1251 | segmentNameCharset: (arg2 != null ? arg2.segmentNameCharset : void 0) || defaultOptions.segmentNameCharset, 1252 | segmentValueCharset: (arg2 != null ? arg2.segmentValueCharset : void 0) || defaultOptions.segmentValueCharset, 1253 | optionalSegmentStartChar: (arg2 != null ? arg2.optionalSegmentStartChar : void 0) || defaultOptions.optionalSegmentStartChar, 1254 | optionalSegmentEndChar: (arg2 != null ? arg2.optionalSegmentEndChar : void 0) || defaultOptions.optionalSegmentEndChar, 1255 | wildcardChar: (arg2 != null ? arg2.wildcardChar : void 0) || defaultOptions.wildcardChar 1256 | }; 1257 | parser = newParser(options); 1258 | parsed = parser.pattern(arg1); 1259 | if (parsed == null) { 1260 | throw new Error("couldn't parse pattern"); 1261 | } 1262 | if (parsed.rest !== '') { 1263 | throw new Error("could only partially parse pattern"); 1264 | } 1265 | this.ast = parsed.value; 1266 | this.regex = new RegExp(astNodeToRegexString(this.ast, options.segmentValueCharset)); 1267 | this.names = astNodeToNames(this.ast); 1268 | }; 1269 | UrlPattern.prototype.match = function(url) { 1270 | var groups, match; 1271 | match = this.regex.exec(url); 1272 | if (match == null) { 1273 | return null; 1274 | } 1275 | groups = match.slice(1); 1276 | if (this.names) { 1277 | return keysAndValuesToObject(this.names, groups); 1278 | } else { 1279 | return groups; 1280 | } 1281 | }; 1282 | UrlPattern.prototype.stringify = function(params) { 1283 | if (params == null) { 1284 | params = {}; 1285 | } 1286 | if (this.isRegex) { 1287 | throw new Error("can't stringify patterns generated from a regex"); 1288 | } 1289 | if (params !== Object(params)) { 1290 | throw new Error("argument must be an object or undefined"); 1291 | } 1292 | return stringify(this.ast, params, {}); 1293 | }; 1294 | UrlPattern.escapeForRegex = escapeForRegex; 1295 | UrlPattern.concatMap = concatMap; 1296 | UrlPattern.stringConcatMap = stringConcatMap; 1297 | UrlPattern.regexGroupCount = regexGroupCount; 1298 | UrlPattern.keysAndValuesToObject = keysAndValuesToObject; 1299 | UrlPattern.P = P; 1300 | UrlPattern.newParser = newParser; 1301 | UrlPattern.defaultOptions = defaultOptions; 1302 | UrlPattern.astNodeToRegexString = astNodeToRegexString; 1303 | UrlPattern.astNodeToNames = astNodeToNames; 1304 | UrlPattern.getParam = getParam; 1305 | UrlPattern.astNodeContainsSegmentsForProvidedParams = astNodeContainsSegmentsForProvidedParams; 1306 | UrlPattern.stringify = stringify; 1307 | return UrlPattern; 1308 | }); 1309 | 1310 | },{}],5:[function(require,module,exports){ 1311 | if (typeof Object.create === 'function') { 1312 | // implementation from standard node.js 'util' module 1313 | module.exports = function inherits(ctor, superCtor) { 1314 | ctor.super_ = superCtor 1315 | ctor.prototype = Object.create(superCtor.prototype, { 1316 | constructor: { 1317 | value: ctor, 1318 | enumerable: false, 1319 | writable: true, 1320 | configurable: true 1321 | } 1322 | }); 1323 | }; 1324 | } else { 1325 | // old school shim for old browsers 1326 | module.exports = function inherits(ctor, superCtor) { 1327 | ctor.super_ = superCtor 1328 | var TempCtor = function () {} 1329 | TempCtor.prototype = superCtor.prototype 1330 | ctor.prototype = new TempCtor() 1331 | ctor.prototype.constructor = ctor 1332 | } 1333 | } 1334 | 1335 | },{}],6:[function(require,module,exports){ 1336 | module.exports = function isBuffer(arg) { 1337 | return arg && typeof arg === 'object' 1338 | && typeof arg.copy === 'function' 1339 | && typeof arg.fill === 'function' 1340 | && typeof arg.readUInt8 === 'function'; 1341 | } 1342 | },{}],7:[function(require,module,exports){ 1343 | (function (process,global){ 1344 | // Copyright Joyent, Inc. and other Node contributors. 1345 | // 1346 | // Permission is hereby granted, free of charge, to any person obtaining a 1347 | // copy of this software and associated documentation files (the 1348 | // "Software"), to deal in the Software without restriction, including 1349 | // without limitation the rights to use, copy, modify, merge, publish, 1350 | // distribute, sublicense, and/or sell copies of the Software, and to permit 1351 | // persons to whom the Software is furnished to do so, subject to the 1352 | // following conditions: 1353 | // 1354 | // The above copyright notice and this permission notice shall be included 1355 | // in all copies or substantial portions of the Software. 1356 | // 1357 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 1358 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 1359 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 1360 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 1361 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 1362 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 1363 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 1364 | 1365 | var formatRegExp = /%[sdj%]/g; 1366 | exports.format = function(f) { 1367 | if (!isString(f)) { 1368 | var objects = []; 1369 | for (var i = 0; i < arguments.length; i++) { 1370 | objects.push(inspect(arguments[i])); 1371 | } 1372 | return objects.join(' '); 1373 | } 1374 | 1375 | var i = 1; 1376 | var args = arguments; 1377 | var len = args.length; 1378 | var str = String(f).replace(formatRegExp, function(x) { 1379 | if (x === '%%') return '%'; 1380 | if (i >= len) return x; 1381 | switch (x) { 1382 | case '%s': return String(args[i++]); 1383 | case '%d': return Number(args[i++]); 1384 | case '%j': 1385 | try { 1386 | return JSON.stringify(args[i++]); 1387 | } catch (_) { 1388 | return '[Circular]'; 1389 | } 1390 | default: 1391 | return x; 1392 | } 1393 | }); 1394 | for (var x = args[i]; i < len; x = args[++i]) { 1395 | if (isNull(x) || !isObject(x)) { 1396 | str += ' ' + x; 1397 | } else { 1398 | str += ' ' + inspect(x); 1399 | } 1400 | } 1401 | return str; 1402 | }; 1403 | 1404 | 1405 | // Mark that a method should not be used. 1406 | // Returns a modified function which warns once by default. 1407 | // If --no-deprecation is set, then it is a no-op. 1408 | exports.deprecate = function(fn, msg) { 1409 | // Allow for deprecating things in the process of starting up. 1410 | if (isUndefined(global.process)) { 1411 | return function() { 1412 | return exports.deprecate(fn, msg).apply(this, arguments); 1413 | }; 1414 | } 1415 | 1416 | if (process.noDeprecation === true) { 1417 | return fn; 1418 | } 1419 | 1420 | var warned = false; 1421 | function deprecated() { 1422 | if (!warned) { 1423 | if (process.throwDeprecation) { 1424 | throw new Error(msg); 1425 | } else if (process.traceDeprecation) { 1426 | console.trace(msg); 1427 | } else { 1428 | console.error(msg); 1429 | } 1430 | warned = true; 1431 | } 1432 | return fn.apply(this, arguments); 1433 | } 1434 | 1435 | return deprecated; 1436 | }; 1437 | 1438 | 1439 | var debugs = {}; 1440 | var debugEnviron; 1441 | exports.debuglog = function(set) { 1442 | if (isUndefined(debugEnviron)) 1443 | debugEnviron = process.env.NODE_DEBUG || ''; 1444 | set = set.toUpperCase(); 1445 | if (!debugs[set]) { 1446 | if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { 1447 | var pid = process.pid; 1448 | debugs[set] = function() { 1449 | var msg = exports.format.apply(exports, arguments); 1450 | console.error('%s %d: %s', set, pid, msg); 1451 | }; 1452 | } else { 1453 | debugs[set] = function() {}; 1454 | } 1455 | } 1456 | return debugs[set]; 1457 | }; 1458 | 1459 | 1460 | /** 1461 | * Echos the value of a value. Trys to print the value out 1462 | * in the best way possible given the different types. 1463 | * 1464 | * @param {Object} obj The object to print out. 1465 | * @param {Object} opts Optional options object that alters the output. 1466 | */ 1467 | /* legacy: obj, showHidden, depth, colors*/ 1468 | function inspect(obj, opts) { 1469 | // default options 1470 | var ctx = { 1471 | seen: [], 1472 | stylize: stylizeNoColor 1473 | }; 1474 | // legacy... 1475 | if (arguments.length >= 3) ctx.depth = arguments[2]; 1476 | if (arguments.length >= 4) ctx.colors = arguments[3]; 1477 | if (isBoolean(opts)) { 1478 | // legacy... 1479 | ctx.showHidden = opts; 1480 | } else if (opts) { 1481 | // got an "options" object 1482 | exports._extend(ctx, opts); 1483 | } 1484 | // set default options 1485 | if (isUndefined(ctx.showHidden)) ctx.showHidden = false; 1486 | if (isUndefined(ctx.depth)) ctx.depth = 2; 1487 | if (isUndefined(ctx.colors)) ctx.colors = false; 1488 | if (isUndefined(ctx.customInspect)) ctx.customInspect = true; 1489 | if (ctx.colors) ctx.stylize = stylizeWithColor; 1490 | return formatValue(ctx, obj, ctx.depth); 1491 | } 1492 | exports.inspect = inspect; 1493 | 1494 | 1495 | // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics 1496 | inspect.colors = { 1497 | 'bold' : [1, 22], 1498 | 'italic' : [3, 23], 1499 | 'underline' : [4, 24], 1500 | 'inverse' : [7, 27], 1501 | 'white' : [37, 39], 1502 | 'grey' : [90, 39], 1503 | 'black' : [30, 39], 1504 | 'blue' : [34, 39], 1505 | 'cyan' : [36, 39], 1506 | 'green' : [32, 39], 1507 | 'magenta' : [35, 39], 1508 | 'red' : [31, 39], 1509 | 'yellow' : [33, 39] 1510 | }; 1511 | 1512 | // Don't use 'blue' not visible on cmd.exe 1513 | inspect.styles = { 1514 | 'special': 'cyan', 1515 | 'number': 'yellow', 1516 | 'boolean': 'yellow', 1517 | 'undefined': 'grey', 1518 | 'null': 'bold', 1519 | 'string': 'green', 1520 | 'date': 'magenta', 1521 | // "name": intentionally not styling 1522 | 'regexp': 'red' 1523 | }; 1524 | 1525 | 1526 | function stylizeWithColor(str, styleType) { 1527 | var style = inspect.styles[styleType]; 1528 | 1529 | if (style) { 1530 | return '\u001b[' + inspect.colors[style][0] + 'm' + str + 1531 | '\u001b[' + inspect.colors[style][1] + 'm'; 1532 | } else { 1533 | return str; 1534 | } 1535 | } 1536 | 1537 | 1538 | function stylizeNoColor(str, styleType) { 1539 | return str; 1540 | } 1541 | 1542 | 1543 | function arrayToHash(array) { 1544 | var hash = {}; 1545 | 1546 | array.forEach(function(val, idx) { 1547 | hash[val] = true; 1548 | }); 1549 | 1550 | return hash; 1551 | } 1552 | 1553 | 1554 | function formatValue(ctx, value, recurseTimes) { 1555 | // Provide a hook for user-specified inspect functions. 1556 | // Check that value is an object with an inspect function on it 1557 | if (ctx.customInspect && 1558 | value && 1559 | isFunction(value.inspect) && 1560 | // Filter out the util module, it's inspect function is special 1561 | value.inspect !== exports.inspect && 1562 | // Also filter out any prototype objects using the circular check. 1563 | !(value.constructor && value.constructor.prototype === value)) { 1564 | var ret = value.inspect(recurseTimes, ctx); 1565 | if (!isString(ret)) { 1566 | ret = formatValue(ctx, ret, recurseTimes); 1567 | } 1568 | return ret; 1569 | } 1570 | 1571 | // Primitive types cannot have properties 1572 | var primitive = formatPrimitive(ctx, value); 1573 | if (primitive) { 1574 | return primitive; 1575 | } 1576 | 1577 | // Look up the keys of the object. 1578 | var keys = Object.keys(value); 1579 | var visibleKeys = arrayToHash(keys); 1580 | 1581 | if (ctx.showHidden) { 1582 | keys = Object.getOwnPropertyNames(value); 1583 | } 1584 | 1585 | // IE doesn't make error fields non-enumerable 1586 | // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx 1587 | if (isError(value) 1588 | && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { 1589 | return formatError(value); 1590 | } 1591 | 1592 | // Some type of object without properties can be shortcutted. 1593 | if (keys.length === 0) { 1594 | if (isFunction(value)) { 1595 | var name = value.name ? ': ' + value.name : ''; 1596 | return ctx.stylize('[Function' + name + ']', 'special'); 1597 | } 1598 | if (isRegExp(value)) { 1599 | return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); 1600 | } 1601 | if (isDate(value)) { 1602 | return ctx.stylize(Date.prototype.toString.call(value), 'date'); 1603 | } 1604 | if (isError(value)) { 1605 | return formatError(value); 1606 | } 1607 | } 1608 | 1609 | var base = '', array = false, braces = ['{', '}']; 1610 | 1611 | // Make Array say that they are Array 1612 | if (isArray(value)) { 1613 | array = true; 1614 | braces = ['[', ']']; 1615 | } 1616 | 1617 | // Make functions say that they are functions 1618 | if (isFunction(value)) { 1619 | var n = value.name ? ': ' + value.name : ''; 1620 | base = ' [Function' + n + ']'; 1621 | } 1622 | 1623 | // Make RegExps say that they are RegExps 1624 | if (isRegExp(value)) { 1625 | base = ' ' + RegExp.prototype.toString.call(value); 1626 | } 1627 | 1628 | // Make dates with properties first say the date 1629 | if (isDate(value)) { 1630 | base = ' ' + Date.prototype.toUTCString.call(value); 1631 | } 1632 | 1633 | // Make error with message first say the error 1634 | if (isError(value)) { 1635 | base = ' ' + formatError(value); 1636 | } 1637 | 1638 | if (keys.length === 0 && (!array || value.length == 0)) { 1639 | return braces[0] + base + braces[1]; 1640 | } 1641 | 1642 | if (recurseTimes < 0) { 1643 | if (isRegExp(value)) { 1644 | return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); 1645 | } else { 1646 | return ctx.stylize('[Object]', 'special'); 1647 | } 1648 | } 1649 | 1650 | ctx.seen.push(value); 1651 | 1652 | var output; 1653 | if (array) { 1654 | output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); 1655 | } else { 1656 | output = keys.map(function(key) { 1657 | return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); 1658 | }); 1659 | } 1660 | 1661 | ctx.seen.pop(); 1662 | 1663 | return reduceToSingleString(output, base, braces); 1664 | } 1665 | 1666 | 1667 | function formatPrimitive(ctx, value) { 1668 | if (isUndefined(value)) 1669 | return ctx.stylize('undefined', 'undefined'); 1670 | if (isString(value)) { 1671 | var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') 1672 | .replace(/'/g, "\\'") 1673 | .replace(/\\"/g, '"') + '\''; 1674 | return ctx.stylize(simple, 'string'); 1675 | } 1676 | if (isNumber(value)) 1677 | return ctx.stylize('' + value, 'number'); 1678 | if (isBoolean(value)) 1679 | return ctx.stylize('' + value, 'boolean'); 1680 | // For some reason typeof null is "object", so special case here. 1681 | if (isNull(value)) 1682 | return ctx.stylize('null', 'null'); 1683 | } 1684 | 1685 | 1686 | function formatError(value) { 1687 | return '[' + Error.prototype.toString.call(value) + ']'; 1688 | } 1689 | 1690 | 1691 | function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { 1692 | var output = []; 1693 | for (var i = 0, l = value.length; i < l; ++i) { 1694 | if (hasOwnProperty(value, String(i))) { 1695 | output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, 1696 | String(i), true)); 1697 | } else { 1698 | output.push(''); 1699 | } 1700 | } 1701 | keys.forEach(function(key) { 1702 | if (!key.match(/^\d+$/)) { 1703 | output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, 1704 | key, true)); 1705 | } 1706 | }); 1707 | return output; 1708 | } 1709 | 1710 | 1711 | function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { 1712 | var name, str, desc; 1713 | desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; 1714 | if (desc.get) { 1715 | if (desc.set) { 1716 | str = ctx.stylize('[Getter/Setter]', 'special'); 1717 | } else { 1718 | str = ctx.stylize('[Getter]', 'special'); 1719 | } 1720 | } else { 1721 | if (desc.set) { 1722 | str = ctx.stylize('[Setter]', 'special'); 1723 | } 1724 | } 1725 | if (!hasOwnProperty(visibleKeys, key)) { 1726 | name = '[' + key + ']'; 1727 | } 1728 | if (!str) { 1729 | if (ctx.seen.indexOf(desc.value) < 0) { 1730 | if (isNull(recurseTimes)) { 1731 | str = formatValue(ctx, desc.value, null); 1732 | } else { 1733 | str = formatValue(ctx, desc.value, recurseTimes - 1); 1734 | } 1735 | if (str.indexOf('\n') > -1) { 1736 | if (array) { 1737 | str = str.split('\n').map(function(line) { 1738 | return ' ' + line; 1739 | }).join('\n').substr(2); 1740 | } else { 1741 | str = '\n' + str.split('\n').map(function(line) { 1742 | return ' ' + line; 1743 | }).join('\n'); 1744 | } 1745 | } 1746 | } else { 1747 | str = ctx.stylize('[Circular]', 'special'); 1748 | } 1749 | } 1750 | if (isUndefined(name)) { 1751 | if (array && key.match(/^\d+$/)) { 1752 | return str; 1753 | } 1754 | name = JSON.stringify('' + key); 1755 | if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { 1756 | name = name.substr(1, name.length - 2); 1757 | name = ctx.stylize(name, 'name'); 1758 | } else { 1759 | name = name.replace(/'/g, "\\'") 1760 | .replace(/\\"/g, '"') 1761 | .replace(/(^"|"$)/g, "'"); 1762 | name = ctx.stylize(name, 'string'); 1763 | } 1764 | } 1765 | 1766 | return name + ': ' + str; 1767 | } 1768 | 1769 | 1770 | function reduceToSingleString(output, base, braces) { 1771 | var numLinesEst = 0; 1772 | var length = output.reduce(function(prev, cur) { 1773 | numLinesEst++; 1774 | if (cur.indexOf('\n') >= 0) numLinesEst++; 1775 | return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; 1776 | }, 0); 1777 | 1778 | if (length > 60) { 1779 | return braces[0] + 1780 | (base === '' ? '' : base + '\n ') + 1781 | ' ' + 1782 | output.join(',\n ') + 1783 | ' ' + 1784 | braces[1]; 1785 | } 1786 | 1787 | return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; 1788 | } 1789 | 1790 | 1791 | // NOTE: These type checking functions intentionally don't use `instanceof` 1792 | // because it is fragile and can be easily faked with `Object.create()`. 1793 | function isArray(ar) { 1794 | return Array.isArray(ar); 1795 | } 1796 | exports.isArray = isArray; 1797 | 1798 | function isBoolean(arg) { 1799 | return typeof arg === 'boolean'; 1800 | } 1801 | exports.isBoolean = isBoolean; 1802 | 1803 | function isNull(arg) { 1804 | return arg === null; 1805 | } 1806 | exports.isNull = isNull; 1807 | 1808 | function isNullOrUndefined(arg) { 1809 | return arg == null; 1810 | } 1811 | exports.isNullOrUndefined = isNullOrUndefined; 1812 | 1813 | function isNumber(arg) { 1814 | return typeof arg === 'number'; 1815 | } 1816 | exports.isNumber = isNumber; 1817 | 1818 | function isString(arg) { 1819 | return typeof arg === 'string'; 1820 | } 1821 | exports.isString = isString; 1822 | 1823 | function isSymbol(arg) { 1824 | return typeof arg === 'symbol'; 1825 | } 1826 | exports.isSymbol = isSymbol; 1827 | 1828 | function isUndefined(arg) { 1829 | return arg === void 0; 1830 | } 1831 | exports.isUndefined = isUndefined; 1832 | 1833 | function isRegExp(re) { 1834 | return isObject(re) && objectToString(re) === '[object RegExp]'; 1835 | } 1836 | exports.isRegExp = isRegExp; 1837 | 1838 | function isObject(arg) { 1839 | return typeof arg === 'object' && arg !== null; 1840 | } 1841 | exports.isObject = isObject; 1842 | 1843 | function isDate(d) { 1844 | return isObject(d) && objectToString(d) === '[object Date]'; 1845 | } 1846 | exports.isDate = isDate; 1847 | 1848 | function isError(e) { 1849 | return isObject(e) && 1850 | (objectToString(e) === '[object Error]' || e instanceof Error); 1851 | } 1852 | exports.isError = isError; 1853 | 1854 | function isFunction(arg) { 1855 | return typeof arg === 'function'; 1856 | } 1857 | exports.isFunction = isFunction; 1858 | 1859 | function isPrimitive(arg) { 1860 | return arg === null || 1861 | typeof arg === 'boolean' || 1862 | typeof arg === 'number' || 1863 | typeof arg === 'string' || 1864 | typeof arg === 'symbol' || // ES6 symbol 1865 | typeof arg === 'undefined'; 1866 | } 1867 | exports.isPrimitive = isPrimitive; 1868 | 1869 | exports.isBuffer = require('./support/isBuffer'); 1870 | 1871 | function objectToString(o) { 1872 | return Object.prototype.toString.call(o); 1873 | } 1874 | 1875 | 1876 | function pad(n) { 1877 | return n < 10 ? '0' + n.toString(10) : n.toString(10); 1878 | } 1879 | 1880 | 1881 | var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 1882 | 'Oct', 'Nov', 'Dec']; 1883 | 1884 | // 26 Feb 16:19:34 1885 | function timestamp() { 1886 | var d = new Date(); 1887 | var time = [pad(d.getHours()), 1888 | pad(d.getMinutes()), 1889 | pad(d.getSeconds())].join(':'); 1890 | return [d.getDate(), months[d.getMonth()], time].join(' '); 1891 | } 1892 | 1893 | 1894 | // log is just a thin wrapper to console.log that prepends a timestamp 1895 | exports.log = function() { 1896 | console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); 1897 | }; 1898 | 1899 | 1900 | /** 1901 | * Inherit the prototype methods from one constructor into another. 1902 | * 1903 | * The Function.prototype.inherits from lang.js rewritten as a standalone 1904 | * function (not on Function.prototype). NOTE: If this file is to be loaded 1905 | * during bootstrapping this function needs to be rewritten using some native 1906 | * functions as prototype setup using normal JavaScript does not work as 1907 | * expected during bootstrapping (see mirror.js in r114903). 1908 | * 1909 | * @param {function} ctor Constructor function which needs to inherit the 1910 | * prototype. 1911 | * @param {function} superCtor Constructor function to inherit prototype from. 1912 | */ 1913 | exports.inherits = require('inherits'); 1914 | 1915 | exports._extend = function(origin, add) { 1916 | // Don't do anything if add isn't an object 1917 | if (!add || !isObject(add)) return origin; 1918 | 1919 | var keys = Object.keys(add); 1920 | var i = keys.length; 1921 | while (i--) { 1922 | origin[keys[i]] = add[keys[i]]; 1923 | } 1924 | return origin; 1925 | }; 1926 | 1927 | function hasOwnProperty(obj, prop) { 1928 | return Object.prototype.hasOwnProperty.call(obj, prop); 1929 | } 1930 | 1931 | }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 1932 | },{"./support/isBuffer":6,"_process":3,"inherits":5}]},{},[1])(1) 1933 | }); --------------------------------------------------------------------------------