Repository: inca/voie Branch: master Commit: ae7f773eb2d0 Files: 28 Total size: 285.0 KB Directory structure: gitextract_06jfnn_h/ ├── .babelrc ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmignore ├── README.md ├── dist/ │ └── voie.js ├── lib/ │ ├── directives.js │ ├── error.js │ ├── index.js │ ├── state-manager.js │ ├── state.js │ ├── transition.js │ └── utils.js ├── package.json ├── src/ │ ├── directives.js │ ├── error.js │ ├── index.js │ ├── state-manager.js │ ├── state.js │ ├── transition.js │ └── utils.js └── test/ ├── .eslintrc ├── karma.dev.conf.js └── specs/ ├── query.js ├── routing.js ├── states.js └── transitions.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .babelrc ================================================ { "presets": ["es2015"], "plugins": ["transform-runtime"] } ================================================ FILE: .eslintignore ================================================ node_modules/ lib/ coverage/ dist/ ================================================ FILE: .eslintrc ================================================ { "env": { "es6": true, "node": true, "browser": true }, "parserOptions": { "sourceType": "module" }, "ecmaFeatures": { "arrowFunctions": true, "blockBindings": true, "destructuring": true, "objectLiteralShorthandMethods": true, "objectLiteralShorthandProperties": true, "templateStrings": true, "classes": true, "modules": true }, "rules": { "brace-style": [2, "1tbs", { "allowSingleLine": true }], "camelcase": 2, "curly": 2, "eqeqeq": [2, "smart"], "guard-for-in": 2, "indent": [2, 2, { "SwitchCase": 1 }], "init-declarations": [2, "always"], "max-nested-callbacks": [2, 4], "no-caller": 2, "no-console": 2, "no-const-assign": 2, "no-else-return": 2, "no-empty": 2, "no-empty-pattern": 2, "no-floating-decimal": 2, "no-lonely-if": 2, "no-mixed-requires": 2, "no-multi-spaces": 2, "no-multiple-empty-lines": [2, { "max": 2 }], "no-param-reassign": 0, "no-process-exit": 0, "no-self-compare": 2, "no-sequences": 2, "no-shadow": 0, "no-undef": 2, "no-undefined": 0, "no-underscore-dangle": 0, "no-unused-vars": [2, { "args": "none", "argsIgnorePattern": "^_" }], "no-use-before-define": 0, "no-useless-concat": 2, "no-var": 2, "prefer-const": 2, "quotes": [2, "single"], "space-before-function-paren": [2, { "anonymous": "never", "named": "never" }], "space-in-parens": [2, "never"], "yoda": [2, "never", { "exceptRange": true }] } } ================================================ FILE: .gitignore ================================================ .idea/ *.ipr *.iml *.iws *.log *.bak *~ ~* .DS_Store .DS_Store? ._* .Spotlight-V100 .Trashes ehthumbs.db Thumbs.db desktop.ini nohup.out node_modules/ coverage/ ================================================ FILE: .npmignore ================================================ .idea/ *.ipr *.iml *.iws *.log *.bak *~ ~* .DS_Store .DS_Store? ._* .Spotlight-V100 .Trashes ehthumbs.db Thumbs.db desktop.ini nohup.out node_modules/ coverage/ test/ site/ ================================================ FILE: README.md ================================================ # Voie.js **Important! Due to [breaking changes](https://github.com/vuejs/vue/issues/2873) in Vue 2.0 current versions of Voie (0.x.x) are now deprecated (feel free to use it with Vue 1.x.x). The 1.x.x will most probably be a major rewrite, so there's no guarantee of backwards compatibility. Sorry.** **Voie** /vwa/ (fr. "way") is a simple router / layout manager for [Vue.js](http://vuejs.org). Use it to build SPAs of your dreams. Current status: **active development** — any feedback is appreciated. Simple example app is available on [GitHub](https://github.com/inca/voie-example) and [live on Netlify](http://voie-example.netlify.com/). [Standalone bundles](dist/) are also available, mostly for using with jsfiddle, jsbin, codepen, etc. (note, Vue.js is **not** included in bundles). You should never use them in real development — use module bundlers instead. ## Core concepts Unlike official [vue-router](https://github.com/vuejs/vue-router) which is organized around URLs, Voie is organized around _states_. Voie-based apps are basically [finite-state machines](https://en.wikipedia.org/wiki/Finite-state_machine). State is simply a _named_ logical "place" within your application. Each state can _optionally_ have: * URL pattern * Vue component * enter hook to populate state with data * leave hook to cleanup things States are organized into hierarchies: child states will inherit parameters and data from parent state. Also, if child state has a component, then it will be rendered at the location specified by parent (or nearest ancestor) state denoted by `` directive. Consider this example: ```es6 app.add('user', { path: '/user/:userId', redirect: 'user.dashboard', // specify "default" sub-state enter: (ctx) => { // can return a Promise return fetch('/user/' + ctx.params.userId) .then(res => res.json()) .then(data = ctx.data.user = data); }, component: { template: '
' } }); app.add('user.dashboard', { component: { template: '

Hello, {{ user.name }}!

' } }); ``` In this example visiting `/user/123` would fetch a user with id `123` from a server and then render following markup (assuming user has name "Alice"): ```html

Hello, Alice!

``` **Note:** [fragment instances](http://vuejs.org/guide/components.html#Fragment_Instance) are not supported as components. In other words, make sure all components contain a single top-level element without flow control directives (`v-if`, `v-for`, etc.) ## Installation Examples assume ES6 and build environment ([browserify](http://browserify.org/) + [babelify](https://github.com/babel/babelify) or [webpack](https://webpack.github.io/) + [babel-loader](https://github.com/babel/babel-loader)) which is a mainstream. ```bash npm i --save voie ``` You also need an [es6-shim](https://github.com/paulmillr/es6-shim) to make everything go smooth in not-so-modern browsers. ## Usage ### State manager Voie app is an instance of `StateManager`. You provide it with `el`, which is an "entry-point" DOM node where views will be entered. ```es6 import { StateManager } from 'voie' export default new StateManager({ el: '#app' // entry point, either a selector or HTMLElement }); ``` It is a good idea to expose state manager instance as a singleton module (i.e. single instance per application) since you will often want to use it in your Vue methods and stores. ### Define states Next thing you want to do is to register some states. Your app will probably contain plenty of states, so you'll need some structure. I prefer "domain-centric" directory structure: ```es6 // states.js import './users'; import './groups'; // ... ``` ```es6 // users/index.js import app from '../app'; import UsersLayout from './layout.vue'; import UsersList from './list.vue'; app.add('users', { component: UsersLayout ... }); app.add('users.list', { component: UsersList, ... }); app.add('users.create', { ... }); app.add('user', { ... }); app.add('user.view', { ... }); app.add('user.edit', { ... }); app.add('user.delete', { ... }); ``` ```es6 // groups/index.js import app from '../app'; app.add('groups', { ... }); // ... ``` Structuring apps is a matter of preference, so you are free to choose whatever suits you best. ### Running Finally, run your state manager like this: ```es6 // index.js import app from './app'; import './states'; app.start(); ``` It will begin listening for history events and match-and-render current route. ## More usage ### States hierarchy States are automatically organized into a tree-like structure. Each state will have a single parent state. "Root" states would have a `null` parent There are two ways of specifying a parent: * using dot character `.` in state name (e.g. `user` -> `user.transaction` -> `user.transaction.details`) * explicitly using `parent` configuration parameter: ```es6 app.add('users', { ... }); app.add('user', { parent: 'users' }); ``` Each way has its own advantages, so it's usually OK to use both styles in the same app. Specifically, use qualified names to outline context (e.g. "User's profile page" would be `user.profile`) or entity-relationship (e.g. "User's transactions list would be `user.transactions`). Specifying `parent` is handy in cases when you want to preserve concise and clean state name while being able to use a different layout or add some global "enter" hook on state subtree. A typical example would be authentication/authorization: ```es6 app.add('root', { ... }); app.add('login', { parent: 'root', ... }); app.add('authenticated', { parent: 'root', enter: ctx => { if (!UserService.isAuthenticated()) { return { redirect: 'login' }; } } }); app.add('users', { parent: 'authenticated', ... }); app.add('groups', { parent: 'authenticated', ... }); ``` ### Navigating states Use `stateManager.go` to navigate programmatically: ```es6 stateManager.go({ name: 'user.dashboard', params: { userId: '123' } }); ``` In templates you can use `v-link` directive with the same semantics: ```html Dashboard ``` In addition to invoking `stateManager.go` it will also update the `href` attribute and apply an `active` class if current state "includes" the state specified by link. Active class name can be customised globally: ```es6 new StateManager({ el: '#el', activeClass: 'highlighted' }); ``` ### Enter / leave State can optionally define `enter` and `leave` hooks which are functions that accept _state context_ object. State context contains: * `params` — a hash of `string` parameters matched from URL pattern, specified explicitly via `stateManager.go(...)` and inherited from parent context * `data` — object where you can write data to be exposed to Vue component and inherited states * `state` — `State` object to which this context corresponds * `parent` — parent context of this object Typical `enter` hook will use `params` to fetch or prepare some data and expose it via `data` object. Both `enter` and `leave` can return a `Promise`, which makes hooks asynchronous. In case of `enter` the component will only be entered when promise is resolved. Example: ```es6 { enter: (ctx) => UserService.findByEmail(ctx.params.email) .then(user => ctx.data.user = user) } ``` ### Before each / after each Additionally one can configure global `beforeEach` and `afterEach` hooks that will be applied before `enter` hooks and after `leave` hook on each state respectively. Global hooks are configured on `StateManager`: ```es6 new StateManager({ beforeEach(ctx) { if (ctx.state.name === 'private') { return { redirect: 'not_allowed' }; } } }); ``` ### Redirecting Enter can optionally redirect to another state by returning (or resolving via promise) an object like this: `{ redirect: 'state.name' }` or `{ redirect: { name: 'state.name', params: {} }`. When `redirect` is returned by `enter` hook the transition will always redirect whenever it enters specified state (even if this state was not a destination). Redirect can also be specified at state configuration level: ```es6 app.add('users', { redirect: 'users.list', // or with params redirect: { name: 'users.list', params: { sort: '+name' } }, // or even function Transition => Promise(stateName) redirect: (transition) => { transition.params.sort = '+name'; return Promise.resolve('users.list'); } }); ``` When `redirect` is specified as state configuration option it will only be effective when moving specifically to this state (in other words, no redirect occurs when transitioning through this state to another one). ### State transitions Consider following components hierarchy: ``` A / \ B D | | C E ``` Going from C to E implies: * leaving state C * leaving state B * entering state D * entering state E By "leaving" we mean: * executing `leave` hook * destroying Vue component, if any * restoring the original state of `` element where the component was rendered By "entering" we mean: * preparing new context * executing `enter` hook * rendering Vue component, if any * preserving the original state of `` so that it could later be restored ### Parameters Each state has a specification of parameters it can accept when entered. Mandatory parameters (e.g. `userId` for state `user`) are classically specified in pathname (e.g. `/user/28`). Optional parameters (e.g. `page`, `limit` for lists) are usually specified in querystring (e.g. `/users?page=5&limit=100`). Here's how you define both parameter types when registering states: ```es6 app.add('user', { path: '/user/:userId', // userId param is mandatory params: { section: null, // these are optional collapsed: false // with optional default values } }); ``` Both querystring and pathname parameters are accessible in `ctx.params` object which is exposed both to `enter` hook and components. Example: ``` location.href = '/user/123?section=profile&collapsed=true'; app.context.params // { userId: '123', section: 'profile', collapsed: 'true' } ``` **Note:** Voie doesn't do any type conversion on params, so they are returned as strings. When navigating between states specify parameters in `go` (or `v-link`): ```es6 app.go({ name: 'user', params: { userId: '123', section: 'profile', unknown: 'wut?' // Important, this will be dropped! } }); ``` **Note:** params must be listed explicitly when registering states, all other parameters will be dropped. In the example above parameter `unknown` is not specified in `path` or `params` of `user` state (or its ancestors), so it's not part of `user` state spec and, therefore, will not be accessible in `ctx.params`. ### History A common need for any web application is to update browser URL upon navigating to a state with URL mapping, so that when the user presses "Refresh" application loads the most recent state (not the "start" screen). Decent SPAs would also have Back/Forward buttons working as expected. We refer to these features as "history support". Now there's two ways of implementing history support in your application: * **hash** (uncool, but fairly simple) — state will be maintained using hash portions of URL (e.g. `https://myapp/#user/1/transactions`) * **HTML5** (cool, a bit more complex) — state will be maintained using pathname portion of URL (e.g. `https://myapp/user/1/transactions`) HTML5 history requires server-side setup: server must reply with the same HTML wrapper to **all URLs** used by your application. #### HTML5 server setup example Here's an example [Express](http://expressjs.com) server: ```es6 import express from 'express'; let app = express(); // Allow using static resources with `/static` prefix app.use('/static', express.static('static')); // Serve application data with `/~` prefix app.use('/~', appDataRouter); // Serve SPA entry-point HTML to all other routes app.get('/*', (req, res) => res.sendFile('app.html')) ``` This example shows potential gotchas: since `app.html` will be served for all GET requests, in order to serve other resources (e.g. static or compiled assets, scripts, stylesheets, application data, etc.) you'll need separate prefixes. Without these prefixes it won't be easy to configure your front web server ([nginx](http://nginx.org/en/) or Apache) for production. #### History setup Voie uses awesome [history](https://github.com/rackt/history) to provide apps with history support. HTML5 mode is used by default. Here's how to switch to hash-history (you need to install `history`): ```es6 // app.js import { StateManager } from 'voie'; import { createHashHistory } from 'history'; export default new StateManager({ el: '#app', history: createHashHistory() }); ``` See [history docs](https://github.com/rackt/history/tree/master/docs) for more options. #### Base URL It's a bit easier to setup servers using "base" URL for your app, e.g.: ``` app.get('/app*', (req, res) => res.sendFile('app.html')) ``` In this case all you need to do is to add `` inside the `` of your HTML wrapper. Or you can just specify `base` configuration parameter (it's only used when `history` is not present): ```es6 // app.js import { StateManager } from 'voie'; export default new StateManager({ el: '#app', base: '/app' }); ``` ### More docs Voie is in active development, so more docs are coming soon. ## License (ISC) Copyright (c) 2015-2016, Boris Okunskiy Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ================================================ FILE: dist/voie.js ================================================ (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.voie = 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 i)if(isEnum.call(it, key = symbols[i++]))keys.push(key); } return keys; }; },{"./$":52}],33:[function(require,module,exports){ var global = require('./$.global') , core = require('./$.core') , ctx = require('./$.ctx') , PROTOTYPE = 'prototype'; var $export = function(type, name, source){ var IS_FORCED = type & $export.F , IS_GLOBAL = type & $export.G , IS_STATIC = type & $export.S , IS_PROTO = type & $export.P , IS_BIND = type & $export.B , IS_WRAP = type & $export.W , exports = IS_GLOBAL ? core : core[name] || (core[name] = {}) , target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE] , key, own, out; if(IS_GLOBAL)source = name; for(key in source){ // contains in native own = !IS_FORCED && target && key in target; if(own && key in exports)continue; // export native or passed out = own ? target[key] : source[key]; // prevent global pollution for namespaces exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key] // bind timers to global for call from export context : IS_BIND && own ? ctx(out, global) // wrap global constructors for prevent change them in library : IS_WRAP && target[key] == out ? (function(C){ var F = function(param){ return this instanceof C ? new C(param) : C(param); }; F[PROTOTYPE] = C[PROTOTYPE]; return F; // make static versions for prototype methods })(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out; if(IS_PROTO)(exports[PROTOTYPE] || (exports[PROTOTYPE] = {}))[key] = out; } }; // type bitmap $export.F = 1; // forced $export.G = 2; // global $export.S = 4; // static $export.P = 8; // proto $export.B = 16; // bind $export.W = 32; // wrap module.exports = $export; },{"./$.core":27,"./$.ctx":28,"./$.global":37}],34:[function(require,module,exports){ module.exports = function(exec){ try { return !!exec(); } catch(e){ return true; } }; },{}],35:[function(require,module,exports){ var ctx = require('./$.ctx') , call = require('./$.iter-call') , isArrayIter = require('./$.is-array-iter') , anObject = require('./$.an-object') , toLength = require('./$.to-length') , getIterFn = require('./core.get-iterator-method'); module.exports = function(iterable, entries, fn, that){ var iterFn = getIterFn(iterable) , f = ctx(fn, that, entries ? 2 : 1) , index = 0 , length, step, iterator; if(typeof iterFn != 'function')throw TypeError(iterable + ' is not iterable!'); // fast case for arrays with default iterator if(isArrayIter(iterFn))for(length = toLength(iterable.length); length > index; index++){ entries ? f(anObject(step = iterable[index])[0], step[1]) : f(iterable[index]); } else for(iterator = iterFn.call(iterable); !(step = iterator.next()).done; ){ call(iterator, f, step.value, entries); } }; },{"./$.an-object":24,"./$.ctx":28,"./$.is-array-iter":43,"./$.iter-call":46,"./$.to-length":72,"./core.get-iterator-method":76}],36:[function(require,module,exports){ // fallback for IE11 buggy Object.getOwnPropertyNames with iframe and window var toIObject = require('./$.to-iobject') , getNames = require('./$').getNames , toString = {}.toString; var windowNames = typeof window == 'object' && Object.getOwnPropertyNames ? Object.getOwnPropertyNames(window) : []; var getWindowNames = function(it){ try { return getNames(it); } catch(e){ return windowNames.slice(); } }; module.exports.get = function getOwnPropertyNames(it){ if(windowNames && toString.call(it) == '[object Window]')return getWindowNames(it); return getNames(toIObject(it)); }; },{"./$":52,"./$.to-iobject":71}],37:[function(require,module,exports){ // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 var global = module.exports = typeof window != 'undefined' && window.Math == Math ? window : typeof self != 'undefined' && self.Math == Math ? self : Function('return this')(); if(typeof __g == 'number')__g = global; // eslint-disable-line no-undef },{}],38:[function(require,module,exports){ var hasOwnProperty = {}.hasOwnProperty; module.exports = function(it, key){ return hasOwnProperty.call(it, key); }; },{}],39:[function(require,module,exports){ var $ = require('./$') , createDesc = require('./$.property-desc'); module.exports = require('./$.descriptors') ? function(object, key, value){ return $.setDesc(object, key, createDesc(1, value)); } : function(object, key, value){ object[key] = value; return object; }; },{"./$":52,"./$.descriptors":30,"./$.property-desc":58}],40:[function(require,module,exports){ module.exports = require('./$.global').document && document.documentElement; },{"./$.global":37}],41:[function(require,module,exports){ // fast apply, http://jsperf.lnkit.com/fast-apply/5 module.exports = function(fn, args, that){ var un = that === undefined; switch(args.length){ case 0: return un ? fn() : fn.call(that); case 1: return un ? fn(args[0]) : fn.call(that, args[0]); case 2: return un ? fn(args[0], args[1]) : fn.call(that, args[0], args[1]); case 3: return un ? fn(args[0], args[1], args[2]) : fn.call(that, args[0], args[1], args[2]); case 4: return un ? fn(args[0], args[1], args[2], args[3]) : fn.call(that, args[0], args[1], args[2], args[3]); } return fn.apply(that, args); }; },{}],42:[function(require,module,exports){ // fallback for non-array-like ES3 and non-enumerable old V8 strings var cof = require('./$.cof'); module.exports = Object('z').propertyIsEnumerable(0) ? Object : function(it){ return cof(it) == 'String' ? it.split('') : Object(it); }; },{"./$.cof":26}],43:[function(require,module,exports){ // check on default Array iterator var Iterators = require('./$.iterators') , ITERATOR = require('./$.wks')('iterator') , ArrayProto = Array.prototype; module.exports = function(it){ return it !== undefined && (Iterators.Array === it || ArrayProto[ITERATOR] === it); }; },{"./$.iterators":51,"./$.wks":75}],44:[function(require,module,exports){ // 7.2.2 IsArray(argument) var cof = require('./$.cof'); module.exports = Array.isArray || function(arg){ return cof(arg) == 'Array'; }; },{"./$.cof":26}],45:[function(require,module,exports){ module.exports = function(it){ return typeof it === 'object' ? it !== null : typeof it === 'function'; }; },{}],46:[function(require,module,exports){ // call something on iterator step with safe closing on error var anObject = require('./$.an-object'); module.exports = function(iterator, fn, value, entries){ try { return entries ? fn(anObject(value)[0], value[1]) : fn(value); // 7.4.6 IteratorClose(iterator, completion) } catch(e){ var ret = iterator['return']; if(ret !== undefined)anObject(ret.call(iterator)); throw e; } }; },{"./$.an-object":24}],47:[function(require,module,exports){ 'use strict'; var $ = require('./$') , descriptor = require('./$.property-desc') , setToStringTag = require('./$.set-to-string-tag') , IteratorPrototype = {}; // 25.1.2.1.1 %IteratorPrototype%[@@iterator]() require('./$.hide')(IteratorPrototype, require('./$.wks')('iterator'), function(){ return this; }); module.exports = function(Constructor, NAME, next){ Constructor.prototype = $.create(IteratorPrototype, {next: descriptor(1, next)}); setToStringTag(Constructor, NAME + ' Iterator'); }; },{"./$":52,"./$.hide":39,"./$.property-desc":58,"./$.set-to-string-tag":64,"./$.wks":75}],48:[function(require,module,exports){ 'use strict'; var LIBRARY = require('./$.library') , $export = require('./$.export') , redefine = require('./$.redefine') , hide = require('./$.hide') , has = require('./$.has') , Iterators = require('./$.iterators') , $iterCreate = require('./$.iter-create') , setToStringTag = require('./$.set-to-string-tag') , getProto = require('./$').getProto , ITERATOR = require('./$.wks')('iterator') , BUGGY = !([].keys && 'next' in [].keys()) // Safari has buggy iterators w/o `next` , FF_ITERATOR = '@@iterator' , KEYS = 'keys' , VALUES = 'values'; var returnThis = function(){ return this; }; module.exports = function(Base, NAME, Constructor, next, DEFAULT, IS_SET, FORCED){ $iterCreate(Constructor, NAME, next); var getMethod = function(kind){ if(!BUGGY && kind in proto)return proto[kind]; switch(kind){ case KEYS: return function keys(){ return new Constructor(this, kind); }; case VALUES: return function values(){ return new Constructor(this, kind); }; } return function entries(){ return new Constructor(this, kind); }; }; var TAG = NAME + ' Iterator' , DEF_VALUES = DEFAULT == VALUES , VALUES_BUG = false , proto = Base.prototype , $native = proto[ITERATOR] || proto[FF_ITERATOR] || DEFAULT && proto[DEFAULT] , $default = $native || getMethod(DEFAULT) , methods, key; // Fix native if($native){ var IteratorPrototype = getProto($default.call(new Base)); // Set @@toStringTag to native iterators setToStringTag(IteratorPrototype, TAG, true); // FF fix if(!LIBRARY && has(proto, FF_ITERATOR))hide(IteratorPrototype, ITERATOR, returnThis); // fix Array#{values, @@iterator}.name in V8 / FF if(DEF_VALUES && $native.name !== VALUES){ VALUES_BUG = true; $default = function values(){ return $native.call(this); }; } } // Define iterator if((!LIBRARY || FORCED) && (BUGGY || VALUES_BUG || !proto[ITERATOR])){ hide(proto, ITERATOR, $default); } // Plug for library Iterators[NAME] = $default; Iterators[TAG] = returnThis; if(DEFAULT){ methods = { values: DEF_VALUES ? $default : getMethod(VALUES), keys: IS_SET ? $default : getMethod(KEYS), entries: !DEF_VALUES ? $default : getMethod('entries') }; if(FORCED)for(key in methods){ if(!(key in proto))redefine(proto, key, methods[key]); } else $export($export.P + $export.F * (BUGGY || VALUES_BUG), NAME, methods); } return methods; }; },{"./$":52,"./$.export":33,"./$.has":38,"./$.hide":39,"./$.iter-create":47,"./$.iterators":51,"./$.library":54,"./$.redefine":60,"./$.set-to-string-tag":64,"./$.wks":75}],49:[function(require,module,exports){ var ITERATOR = require('./$.wks')('iterator') , SAFE_CLOSING = false; try { var riter = [7][ITERATOR](); riter['return'] = function(){ SAFE_CLOSING = true; }; Array.from(riter, function(){ throw 2; }); } catch(e){ /* empty */ } module.exports = function(exec, skipClosing){ if(!skipClosing && !SAFE_CLOSING)return false; var safe = false; try { var arr = [7] , iter = arr[ITERATOR](); iter.next = function(){ safe = true; }; arr[ITERATOR] = function(){ return iter; }; exec(arr); } catch(e){ /* empty */ } return safe; }; },{"./$.wks":75}],50:[function(require,module,exports){ module.exports = function(done, value){ return {value: value, done: !!done}; }; },{}],51:[function(require,module,exports){ module.exports = {}; },{}],52:[function(require,module,exports){ var $Object = Object; module.exports = { create: $Object.create, getProto: $Object.getPrototypeOf, isEnum: {}.propertyIsEnumerable, getDesc: $Object.getOwnPropertyDescriptor, setDesc: $Object.defineProperty, setDescs: $Object.defineProperties, getKeys: $Object.keys, getNames: $Object.getOwnPropertyNames, getSymbols: $Object.getOwnPropertySymbols, each: [].forEach }; },{}],53:[function(require,module,exports){ var $ = require('./$') , toIObject = require('./$.to-iobject'); module.exports = function(object, el){ var O = toIObject(object) , keys = $.getKeys(O) , length = keys.length , index = 0 , key; while(length > index)if(O[key = keys[index++]] === el)return key; }; },{"./$":52,"./$.to-iobject":71}],54:[function(require,module,exports){ module.exports = true; },{}],55:[function(require,module,exports){ var global = require('./$.global') , macrotask = require('./$.task').set , Observer = global.MutationObserver || global.WebKitMutationObserver , process = global.process , Promise = global.Promise , isNode = require('./$.cof')(process) == 'process' , head, last, notify; var flush = function(){ var parent, domain, fn; if(isNode && (parent = process.domain)){ process.domain = null; parent.exit(); } while(head){ domain = head.domain; fn = head.fn; if(domain)domain.enter(); fn(); // <- currently we use it only for Promise - try / catch not required if(domain)domain.exit(); head = head.next; } last = undefined; if(parent)parent.enter(); }; // Node.js if(isNode){ notify = function(){ process.nextTick(flush); }; // browsers with MutationObserver } else if(Observer){ var toggle = 1 , node = document.createTextNode(''); new Observer(flush).observe(node, {characterData: true}); // eslint-disable-line no-new notify = function(){ node.data = toggle = -toggle; }; // environments with maybe non-completely correct, but existent Promise } else if(Promise && Promise.resolve){ notify = function(){ Promise.resolve().then(flush); }; // for other environments - macrotask based on: // - setImmediate // - MessageChannel // - window.postMessag // - onreadystatechange // - setTimeout } else { notify = function(){ // strange IE + webpack dev server bug - use .call(global) macrotask.call(global, flush); }; } module.exports = function asap(fn){ var task = {fn: fn, next: undefined, domain: isNode && process.domain}; if(last)last.next = task; if(!head){ head = task; notify(); } last = task; }; },{"./$.cof":26,"./$.global":37,"./$.task":69}],56:[function(require,module,exports){ // 19.1.2.1 Object.assign(target, source, ...) var $ = require('./$') , toObject = require('./$.to-object') , IObject = require('./$.iobject'); // should work with symbols and should have deterministic property order (V8 bug) module.exports = require('./$.fails')(function(){ var a = Object.assign , A = {} , B = {} , S = Symbol() , K = 'abcdefghijklmnopqrst'; A[S] = 7; K.split('').forEach(function(k){ B[k] = k; }); return a({}, A)[S] != 7 || Object.keys(a({}, B)).join('') != K; }) ? function assign(target, source){ // eslint-disable-line no-unused-vars var T = toObject(target) , $$ = arguments , $$len = $$.length , index = 1 , getKeys = $.getKeys , getSymbols = $.getSymbols , isEnum = $.isEnum; while($$len > index){ var S = IObject($$[index++]) , keys = getSymbols ? getKeys(S).concat(getSymbols(S)) : getKeys(S) , length = keys.length , j = 0 , key; while(length > j)if(isEnum.call(S, key = keys[j++]))T[key] = S[key]; } return T; } : Object.assign; },{"./$":52,"./$.fails":34,"./$.iobject":42,"./$.to-object":73}],57:[function(require,module,exports){ // most Object methods by ES6 should accept primitives var $export = require('./$.export') , core = require('./$.core') , fails = require('./$.fails'); module.exports = function(KEY, exec){ var fn = (core.Object || {})[KEY] || Object[KEY] , exp = {}; exp[KEY] = exec(fn); $export($export.S + $export.F * fails(function(){ fn(1); }), 'Object', exp); }; },{"./$.core":27,"./$.export":33,"./$.fails":34}],58:[function(require,module,exports){ module.exports = function(bitmap, value){ return { enumerable : !(bitmap & 1), configurable: !(bitmap & 2), writable : !(bitmap & 4), value : value }; }; },{}],59:[function(require,module,exports){ var redefine = require('./$.redefine'); module.exports = function(target, src){ for(var key in src)redefine(target, key, src[key]); return target; }; },{"./$.redefine":60}],60:[function(require,module,exports){ module.exports = require('./$.hide'); },{"./$.hide":39}],61:[function(require,module,exports){ // 7.2.9 SameValue(x, y) module.exports = Object.is || function is(x, y){ return x === y ? x !== 0 || 1 / x === 1 / y : x != x && y != y; }; },{}],62:[function(require,module,exports){ // Works with __proto__ only. Old v8 can't work with null proto objects. /* eslint-disable no-proto */ var getDesc = require('./$').getDesc , isObject = require('./$.is-object') , anObject = require('./$.an-object'); var check = function(O, proto){ anObject(O); if(!isObject(proto) && proto !== null)throw TypeError(proto + ": can't set as prototype!"); }; module.exports = { set: Object.setPrototypeOf || ('__proto__' in {} ? // eslint-disable-line function(test, buggy, set){ try { set = require('./$.ctx')(Function.call, getDesc(Object.prototype, '__proto__').set, 2); set(test, []); buggy = !(test instanceof Array); } catch(e){ buggy = true; } return function setPrototypeOf(O, proto){ check(O, proto); if(buggy)O.__proto__ = proto; else set(O, proto); return O; }; }({}, false) : undefined), check: check }; },{"./$":52,"./$.an-object":24,"./$.ctx":28,"./$.is-object":45}],63:[function(require,module,exports){ 'use strict'; var core = require('./$.core') , $ = require('./$') , DESCRIPTORS = require('./$.descriptors') , SPECIES = require('./$.wks')('species'); module.exports = function(KEY){ var C = core[KEY]; if(DESCRIPTORS && C && !C[SPECIES])$.setDesc(C, SPECIES, { configurable: true, get: function(){ return this; } }); }; },{"./$":52,"./$.core":27,"./$.descriptors":30,"./$.wks":75}],64:[function(require,module,exports){ var def = require('./$').setDesc , has = require('./$.has') , TAG = require('./$.wks')('toStringTag'); module.exports = function(it, tag, stat){ if(it && !has(it = stat ? it : it.prototype, TAG))def(it, TAG, {configurable: true, value: tag}); }; },{"./$":52,"./$.has":38,"./$.wks":75}],65:[function(require,module,exports){ var global = require('./$.global') , SHARED = '__core-js_shared__' , store = global[SHARED] || (global[SHARED] = {}); module.exports = function(key){ return store[key] || (store[key] = {}); }; },{"./$.global":37}],66:[function(require,module,exports){ // 7.3.20 SpeciesConstructor(O, defaultConstructor) var anObject = require('./$.an-object') , aFunction = require('./$.a-function') , SPECIES = require('./$.wks')('species'); module.exports = function(O, D){ var C = anObject(O).constructor, S; return C === undefined || (S = anObject(C)[SPECIES]) == undefined ? D : aFunction(S); }; },{"./$.a-function":22,"./$.an-object":24,"./$.wks":75}],67:[function(require,module,exports){ module.exports = function(it, Constructor, name){ if(!(it instanceof Constructor))throw TypeError(name + ": use the 'new' operator!"); return it; }; },{}],68:[function(require,module,exports){ var toInteger = require('./$.to-integer') , defined = require('./$.defined'); // true -> String#at // false -> String#codePointAt module.exports = function(TO_STRING){ return function(that, pos){ var s = String(defined(that)) , i = toInteger(pos) , l = s.length , a, b; if(i < 0 || i >= l)return TO_STRING ? '' : undefined; a = s.charCodeAt(i); return a < 0xd800 || a > 0xdbff || i + 1 === l || (b = s.charCodeAt(i + 1)) < 0xdc00 || b > 0xdfff ? TO_STRING ? s.charAt(i) : a : TO_STRING ? s.slice(i, i + 2) : (a - 0xd800 << 10) + (b - 0xdc00) + 0x10000; }; }; },{"./$.defined":29,"./$.to-integer":70}],69:[function(require,module,exports){ var ctx = require('./$.ctx') , invoke = require('./$.invoke') , html = require('./$.html') , cel = require('./$.dom-create') , global = require('./$.global') , process = global.process , setTask = global.setImmediate , clearTask = global.clearImmediate , MessageChannel = global.MessageChannel , counter = 0 , queue = {} , ONREADYSTATECHANGE = 'onreadystatechange' , defer, channel, port; var run = function(){ var id = +this; if(queue.hasOwnProperty(id)){ var fn = queue[id]; delete queue[id]; fn(); } }; var listner = function(event){ run.call(event.data); }; // Node.js 0.9+ & IE10+ has setImmediate, otherwise: if(!setTask || !clearTask){ setTask = function setImmediate(fn){ var args = [], i = 1; while(arguments.length > i)args.push(arguments[i++]); queue[++counter] = function(){ invoke(typeof fn == 'function' ? fn : Function(fn), args); }; defer(counter); return counter; }; clearTask = function clearImmediate(id){ delete queue[id]; }; // Node.js 0.8- if(require('./$.cof')(process) == 'process'){ defer = function(id){ process.nextTick(ctx(run, id, 1)); }; // Browsers with MessageChannel, includes WebWorkers } else if(MessageChannel){ channel = new MessageChannel; port = channel.port2; channel.port1.onmessage = listner; defer = ctx(port.postMessage, port, 1); // Browsers with postMessage, skip WebWorkers // IE8 has postMessage, but it's sync & typeof its postMessage is 'object' } else if(global.addEventListener && typeof postMessage == 'function' && !global.importScripts){ defer = function(id){ global.postMessage(id + '', '*'); }; global.addEventListener('message', listner, false); // IE8- } else if(ONREADYSTATECHANGE in cel('script')){ defer = function(id){ html.appendChild(cel('script'))[ONREADYSTATECHANGE] = function(){ html.removeChild(this); run.call(id); }; }; // Rest old browsers } else { defer = function(id){ setTimeout(ctx(run, id, 1), 0); }; } } module.exports = { set: setTask, clear: clearTask }; },{"./$.cof":26,"./$.ctx":28,"./$.dom-create":31,"./$.global":37,"./$.html":40,"./$.invoke":41}],70:[function(require,module,exports){ // 7.1.4 ToInteger var ceil = Math.ceil , floor = Math.floor; module.exports = function(it){ return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it); }; },{}],71:[function(require,module,exports){ // to indexed object, toObject with fallback for non-array-like ES3 strings var IObject = require('./$.iobject') , defined = require('./$.defined'); module.exports = function(it){ return IObject(defined(it)); }; },{"./$.defined":29,"./$.iobject":42}],72:[function(require,module,exports){ // 7.1.15 ToLength var toInteger = require('./$.to-integer') , min = Math.min; module.exports = function(it){ return it > 0 ? min(toInteger(it), 0x1fffffffffffff) : 0; // pow(2, 53) - 1 == 9007199254740991 }; },{"./$.to-integer":70}],73:[function(require,module,exports){ // 7.1.13 ToObject(argument) var defined = require('./$.defined'); module.exports = function(it){ return Object(defined(it)); }; },{"./$.defined":29}],74:[function(require,module,exports){ var id = 0 , px = Math.random(); module.exports = function(key){ return 'Symbol('.concat(key === undefined ? '' : key, ')_', (++id + px).toString(36)); }; },{}],75:[function(require,module,exports){ var store = require('./$.shared')('wks') , uid = require('./$.uid') , Symbol = require('./$.global').Symbol; module.exports = function(name){ return store[name] || (store[name] = Symbol && Symbol[name] || (Symbol || uid)('Symbol.' + name)); }; },{"./$.global":37,"./$.shared":65,"./$.uid":74}],76:[function(require,module,exports){ var classof = require('./$.classof') , ITERATOR = require('./$.wks')('iterator') , Iterators = require('./$.iterators'); module.exports = require('./$.core').getIteratorMethod = function(it){ if(it != undefined)return it[ITERATOR] || it['@@iterator'] || Iterators[classof(it)]; }; },{"./$.classof":25,"./$.core":27,"./$.iterators":51,"./$.wks":75}],77:[function(require,module,exports){ 'use strict'; var addToUnscopables = require('./$.add-to-unscopables') , step = require('./$.iter-step') , Iterators = require('./$.iterators') , toIObject = require('./$.to-iobject'); // 22.1.3.4 Array.prototype.entries() // 22.1.3.13 Array.prototype.keys() // 22.1.3.29 Array.prototype.values() // 22.1.3.30 Array.prototype[@@iterator]() module.exports = require('./$.iter-define')(Array, 'Array', function(iterated, kind){ this._t = toIObject(iterated); // target this._i = 0; // next index this._k = kind; // kind // 22.1.5.2.1 %ArrayIteratorPrototype%.next() }, function(){ var O = this._t , kind = this._k , index = this._i++; if(!O || index >= O.length){ this._t = undefined; return step(1); } if(kind == 'keys' )return step(0, index); if(kind == 'values')return step(0, O[index]); return step(0, [index, O[index]]); }, 'values'); // argumentsList[@@iterator] is %ArrayProto_values% (9.4.4.6, 9.4.4.7) Iterators.Arguments = Iterators.Array; addToUnscopables('keys'); addToUnscopables('values'); addToUnscopables('entries'); },{"./$.add-to-unscopables":23,"./$.iter-define":48,"./$.iter-step":50,"./$.iterators":51,"./$.to-iobject":71}],78:[function(require,module,exports){ // 19.1.3.1 Object.assign(target, source) var $export = require('./$.export'); $export($export.S + $export.F, 'Object', {assign: require('./$.object-assign')}); },{"./$.export":33,"./$.object-assign":56}],79:[function(require,module,exports){ // 19.1.2.9 Object.getPrototypeOf(O) var toObject = require('./$.to-object'); require('./$.object-sap')('getPrototypeOf', function($getPrototypeOf){ return function getPrototypeOf(it){ return $getPrototypeOf(toObject(it)); }; }); },{"./$.object-sap":57,"./$.to-object":73}],80:[function(require,module,exports){ // 19.1.2.14 Object.keys(O) var toObject = require('./$.to-object'); require('./$.object-sap')('keys', function($keys){ return function keys(it){ return $keys(toObject(it)); }; }); },{"./$.object-sap":57,"./$.to-object":73}],81:[function(require,module,exports){ // 19.1.3.19 Object.setPrototypeOf(O, proto) var $export = require('./$.export'); $export($export.S, 'Object', {setPrototypeOf: require('./$.set-proto').set}); },{"./$.export":33,"./$.set-proto":62}],82:[function(require,module,exports){ },{}],83:[function(require,module,exports){ 'use strict'; var $ = require('./$') , LIBRARY = require('./$.library') , global = require('./$.global') , ctx = require('./$.ctx') , classof = require('./$.classof') , $export = require('./$.export') , isObject = require('./$.is-object') , anObject = require('./$.an-object') , aFunction = require('./$.a-function') , strictNew = require('./$.strict-new') , forOf = require('./$.for-of') , setProto = require('./$.set-proto').set , same = require('./$.same-value') , SPECIES = require('./$.wks')('species') , speciesConstructor = require('./$.species-constructor') , asap = require('./$.microtask') , PROMISE = 'Promise' , process = global.process , isNode = classof(process) == 'process' , P = global[PROMISE] , Wrapper; var testResolve = function(sub){ var test = new P(function(){}); if(sub)test.constructor = Object; return P.resolve(test) === test; }; var USE_NATIVE = function(){ var works = false; function P2(x){ var self = new P(x); setProto(self, P2.prototype); return self; } try { works = P && P.resolve && testResolve(); setProto(P2, P); P2.prototype = $.create(P.prototype, {constructor: {value: P2}}); // actual Firefox has broken subclass support, test that if(!(P2.resolve(5).then(function(){}) instanceof P2)){ works = false; } // actual V8 bug, https://code.google.com/p/v8/issues/detail?id=4162 if(works && require('./$.descriptors')){ var thenableThenGotten = false; P.resolve($.setDesc({}, 'then', { get: function(){ thenableThenGotten = true; } })); works = thenableThenGotten; } } catch(e){ works = false; } return works; }(); // helpers var sameConstructor = function(a, b){ // library wrapper special case if(LIBRARY && a === P && b === Wrapper)return true; return same(a, b); }; var getConstructor = function(C){ var S = anObject(C)[SPECIES]; return S != undefined ? S : C; }; var isThenable = function(it){ var then; return isObject(it) && typeof (then = it.then) == 'function' ? then : false; }; var PromiseCapability = function(C){ var resolve, reject; this.promise = new C(function($$resolve, $$reject){ if(resolve !== undefined || reject !== undefined)throw TypeError('Bad Promise constructor'); resolve = $$resolve; reject = $$reject; }); this.resolve = aFunction(resolve), this.reject = aFunction(reject) }; var perform = function(exec){ try { exec(); } catch(e){ return {error: e}; } }; var notify = function(record, isReject){ if(record.n)return; record.n = true; var chain = record.c; asap(function(){ var value = record.v , ok = record.s == 1 , i = 0; var run = function(reaction){ var handler = ok ? reaction.ok : reaction.fail , resolve = reaction.resolve , reject = reaction.reject , result, then; try { if(handler){ if(!ok)record.h = true; result = handler === true ? value : handler(value); if(result === reaction.promise){ reject(TypeError('Promise-chain cycle')); } else if(then = isThenable(result)){ then.call(result, resolve, reject); } else resolve(result); } else reject(value); } catch(e){ reject(e); } }; while(chain.length > i)run(chain[i++]); // variable length - can't use forEach chain.length = 0; record.n = false; if(isReject)setTimeout(function(){ var promise = record.p , handler, console; if(isUnhandled(promise)){ if(isNode){ process.emit('unhandledRejection', value, promise); } else if(handler = global.onunhandledrejection){ handler({promise: promise, reason: value}); } else if((console = global.console) && console.error){ console.error('Unhandled promise rejection', value); } } record.a = undefined; }, 1); }); }; var isUnhandled = function(promise){ var record = promise._d , chain = record.a || record.c , i = 0 , reaction; if(record.h)return false; while(chain.length > i){ reaction = chain[i++]; if(reaction.fail || !isUnhandled(reaction.promise))return false; } return true; }; var $reject = function(value){ var record = this; if(record.d)return; record.d = true; record = record.r || record; // unwrap record.v = value; record.s = 2; record.a = record.c.slice(); notify(record, true); }; var $resolve = function(value){ var record = this , then; if(record.d)return; record.d = true; record = record.r || record; // unwrap try { if(record.p === value)throw TypeError("Promise can't be resolved itself"); if(then = isThenable(value)){ asap(function(){ var wrapper = {r: record, d: false}; // wrap try { then.call(value, ctx($resolve, wrapper, 1), ctx($reject, wrapper, 1)); } catch(e){ $reject.call(wrapper, e); } }); } else { record.v = value; record.s = 1; notify(record, false); } } catch(e){ $reject.call({r: record, d: false}, e); // wrap } }; // constructor polyfill if(!USE_NATIVE){ // 25.4.3.1 Promise(executor) P = function Promise(executor){ aFunction(executor); var record = this._d = { p: strictNew(this, P, PROMISE), // <- promise c: [], // <- awaiting reactions a: undefined, // <- checked in isUnhandled reactions s: 0, // <- state d: false, // <- done v: undefined, // <- value h: false, // <- handled rejection n: false // <- notify }; try { executor(ctx($resolve, record, 1), ctx($reject, record, 1)); } catch(err){ $reject.call(record, err); } }; require('./$.redefine-all')(P.prototype, { // 25.4.5.3 Promise.prototype.then(onFulfilled, onRejected) then: function then(onFulfilled, onRejected){ var reaction = new PromiseCapability(speciesConstructor(this, P)) , promise = reaction.promise , record = this._d; reaction.ok = typeof onFulfilled == 'function' ? onFulfilled : true; reaction.fail = typeof onRejected == 'function' && onRejected; record.c.push(reaction); if(record.a)record.a.push(reaction); if(record.s)notify(record, false); return promise; }, // 25.4.5.1 Promise.prototype.catch(onRejected) 'catch': function(onRejected){ return this.then(undefined, onRejected); } }); } $export($export.G + $export.W + $export.F * !USE_NATIVE, {Promise: P}); require('./$.set-to-string-tag')(P, PROMISE); require('./$.set-species')(PROMISE); Wrapper = require('./$.core')[PROMISE]; // statics $export($export.S + $export.F * !USE_NATIVE, PROMISE, { // 25.4.4.5 Promise.reject(r) reject: function reject(r){ var capability = new PromiseCapability(this) , $$reject = capability.reject; $$reject(r); return capability.promise; } }); $export($export.S + $export.F * (!USE_NATIVE || testResolve(true)), PROMISE, { // 25.4.4.6 Promise.resolve(x) resolve: function resolve(x){ // instanceof instead of internal slot check because we should fix it without replacement native Promise core if(x instanceof P && sameConstructor(x.constructor, this))return x; var capability = new PromiseCapability(this) , $$resolve = capability.resolve; $$resolve(x); return capability.promise; } }); $export($export.S + $export.F * !(USE_NATIVE && require('./$.iter-detect')(function(iter){ P.all(iter)['catch'](function(){}); })), PROMISE, { // 25.4.4.1 Promise.all(iterable) all: function all(iterable){ var C = getConstructor(this) , capability = new PromiseCapability(C) , resolve = capability.resolve , reject = capability.reject , values = []; var abrupt = perform(function(){ forOf(iterable, false, values.push, values); var remaining = values.length , results = Array(remaining); if(remaining)$.each.call(values, function(promise, index){ var alreadyCalled = false; C.resolve(promise).then(function(value){ if(alreadyCalled)return; alreadyCalled = true; results[index] = value; --remaining || resolve(results); }, reject); }); else resolve(results); }); if(abrupt)reject(abrupt.error); return capability.promise; }, // 25.4.4.4 Promise.race(iterable) race: function race(iterable){ var C = getConstructor(this) , capability = new PromiseCapability(C) , reject = capability.reject; var abrupt = perform(function(){ forOf(iterable, false, function(promise){ C.resolve(promise).then(capability.resolve, reject); }); }); if(abrupt)reject(abrupt.error); return capability.promise; } }); },{"./$":52,"./$.a-function":22,"./$.an-object":24,"./$.classof":25,"./$.core":27,"./$.ctx":28,"./$.descriptors":30,"./$.export":33,"./$.for-of":35,"./$.global":37,"./$.is-object":45,"./$.iter-detect":49,"./$.library":54,"./$.microtask":55,"./$.redefine-all":59,"./$.same-value":61,"./$.set-proto":62,"./$.set-species":63,"./$.set-to-string-tag":64,"./$.species-constructor":66,"./$.strict-new":67,"./$.wks":75}],84:[function(require,module,exports){ 'use strict'; var $at = require('./$.string-at')(true); // 21.1.3.27 String.prototype[@@iterator]() require('./$.iter-define')(String, 'String', function(iterated){ this._t = String(iterated); // target this._i = 0; // next index // 21.1.5.2.1 %StringIteratorPrototype%.next() }, function(){ var O = this._t , index = this._i , point; if(index >= O.length)return {value: undefined, done: true}; point = $at(O, index); this._i += point.length; return {value: point, done: false}; }); },{"./$.iter-define":48,"./$.string-at":68}],85:[function(require,module,exports){ 'use strict'; // ECMAScript 6 symbols shim var $ = require('./$') , global = require('./$.global') , has = require('./$.has') , DESCRIPTORS = require('./$.descriptors') , $export = require('./$.export') , redefine = require('./$.redefine') , $fails = require('./$.fails') , shared = require('./$.shared') , setToStringTag = require('./$.set-to-string-tag') , uid = require('./$.uid') , wks = require('./$.wks') , keyOf = require('./$.keyof') , $names = require('./$.get-names') , enumKeys = require('./$.enum-keys') , isArray = require('./$.is-array') , anObject = require('./$.an-object') , toIObject = require('./$.to-iobject') , createDesc = require('./$.property-desc') , getDesc = $.getDesc , setDesc = $.setDesc , _create = $.create , getNames = $names.get , $Symbol = global.Symbol , $JSON = global.JSON , _stringify = $JSON && $JSON.stringify , setter = false , HIDDEN = wks('_hidden') , isEnum = $.isEnum , SymbolRegistry = shared('symbol-registry') , AllSymbols = shared('symbols') , useNative = typeof $Symbol == 'function' , ObjectProto = Object.prototype; // fallback for old Android, https://code.google.com/p/v8/issues/detail?id=687 var setSymbolDesc = DESCRIPTORS && $fails(function(){ return _create(setDesc({}, 'a', { get: function(){ return setDesc(this, 'a', {value: 7}).a; } })).a != 7; }) ? function(it, key, D){ var protoDesc = getDesc(ObjectProto, key); if(protoDesc)delete ObjectProto[key]; setDesc(it, key, D); if(protoDesc && it !== ObjectProto)setDesc(ObjectProto, key, protoDesc); } : setDesc; var wrap = function(tag){ var sym = AllSymbols[tag] = _create($Symbol.prototype); sym._k = tag; DESCRIPTORS && setter && setSymbolDesc(ObjectProto, tag, { configurable: true, set: function(value){ if(has(this, HIDDEN) && has(this[HIDDEN], tag))this[HIDDEN][tag] = false; setSymbolDesc(this, tag, createDesc(1, value)); } }); return sym; }; var isSymbol = function(it){ return typeof it == 'symbol'; }; var $defineProperty = function defineProperty(it, key, D){ if(D && has(AllSymbols, key)){ if(!D.enumerable){ if(!has(it, HIDDEN))setDesc(it, HIDDEN, createDesc(1, {})); it[HIDDEN][key] = true; } else { if(has(it, HIDDEN) && it[HIDDEN][key])it[HIDDEN][key] = false; D = _create(D, {enumerable: createDesc(0, false)}); } return setSymbolDesc(it, key, D); } return setDesc(it, key, D); }; var $defineProperties = function defineProperties(it, P){ anObject(it); var keys = enumKeys(P = toIObject(P)) , i = 0 , l = keys.length , key; while(l > i)$defineProperty(it, key = keys[i++], P[key]); return it; }; var $create = function create(it, P){ return P === undefined ? _create(it) : $defineProperties(_create(it), P); }; var $propertyIsEnumerable = function propertyIsEnumerable(key){ var E = isEnum.call(this, key); return E || !has(this, key) || !has(AllSymbols, key) || has(this, HIDDEN) && this[HIDDEN][key] ? E : true; }; var $getOwnPropertyDescriptor = function getOwnPropertyDescriptor(it, key){ var D = getDesc(it = toIObject(it), key); if(D && has(AllSymbols, key) && !(has(it, HIDDEN) && it[HIDDEN][key]))D.enumerable = true; return D; }; var $getOwnPropertyNames = function getOwnPropertyNames(it){ var names = getNames(toIObject(it)) , result = [] , i = 0 , key; while(names.length > i)if(!has(AllSymbols, key = names[i++]) && key != HIDDEN)result.push(key); return result; }; var $getOwnPropertySymbols = function getOwnPropertySymbols(it){ var names = getNames(toIObject(it)) , result = [] , i = 0 , key; while(names.length > i)if(has(AllSymbols, key = names[i++]))result.push(AllSymbols[key]); return result; }; var $stringify = function stringify(it){ if(it === undefined || isSymbol(it))return; // IE8 returns string on undefined var args = [it] , i = 1 , $$ = arguments , replacer, $replacer; while($$.length > i)args.push($$[i++]); replacer = args[1]; if(typeof replacer == 'function')$replacer = replacer; if($replacer || !isArray(replacer))replacer = function(key, value){ if($replacer)value = $replacer.call(this, key, value); if(!isSymbol(value))return value; }; args[1] = replacer; return _stringify.apply($JSON, args); }; var buggyJSON = $fails(function(){ var S = $Symbol(); // MS Edge converts symbol values to JSON as {} // WebKit converts symbol values to JSON as null // V8 throws on boxed symbols return _stringify([S]) != '[null]' || _stringify({a: S}) != '{}' || _stringify(Object(S)) != '{}'; }); // 19.4.1.1 Symbol([description]) if(!useNative){ $Symbol = function Symbol(){ if(isSymbol(this))throw TypeError('Symbol is not a constructor'); return wrap(uid(arguments.length > 0 ? arguments[0] : undefined)); }; redefine($Symbol.prototype, 'toString', function toString(){ return this._k; }); isSymbol = function(it){ return it instanceof $Symbol; }; $.create = $create; $.isEnum = $propertyIsEnumerable; $.getDesc = $getOwnPropertyDescriptor; $.setDesc = $defineProperty; $.setDescs = $defineProperties; $.getNames = $names.get = $getOwnPropertyNames; $.getSymbols = $getOwnPropertySymbols; if(DESCRIPTORS && !require('./$.library')){ redefine(ObjectProto, 'propertyIsEnumerable', $propertyIsEnumerable, true); } } var symbolStatics = { // 19.4.2.1 Symbol.for(key) 'for': function(key){ return has(SymbolRegistry, key += '') ? SymbolRegistry[key] : SymbolRegistry[key] = $Symbol(key); }, // 19.4.2.5 Symbol.keyFor(sym) keyFor: function keyFor(key){ return keyOf(SymbolRegistry, key); }, useSetter: function(){ setter = true; }, useSimple: function(){ setter = false; } }; // 19.4.2.2 Symbol.hasInstance // 19.4.2.3 Symbol.isConcatSpreadable // 19.4.2.4 Symbol.iterator // 19.4.2.6 Symbol.match // 19.4.2.8 Symbol.replace // 19.4.2.9 Symbol.search // 19.4.2.10 Symbol.species // 19.4.2.11 Symbol.split // 19.4.2.12 Symbol.toPrimitive // 19.4.2.13 Symbol.toStringTag // 19.4.2.14 Symbol.unscopables $.each.call(( 'hasInstance,isConcatSpreadable,iterator,match,replace,search,' + 'species,split,toPrimitive,toStringTag,unscopables' ).split(','), function(it){ var sym = wks(it); symbolStatics[it] = useNative ? sym : wrap(sym); }); setter = true; $export($export.G + $export.W, {Symbol: $Symbol}); $export($export.S, 'Symbol', symbolStatics); $export($export.S + $export.F * !useNative, 'Object', { // 19.1.2.2 Object.create(O [, Properties]) create: $create, // 19.1.2.4 Object.defineProperty(O, P, Attributes) defineProperty: $defineProperty, // 19.1.2.3 Object.defineProperties(O, Properties) defineProperties: $defineProperties, // 19.1.2.6 Object.getOwnPropertyDescriptor(O, P) getOwnPropertyDescriptor: $getOwnPropertyDescriptor, // 19.1.2.7 Object.getOwnPropertyNames(O) getOwnPropertyNames: $getOwnPropertyNames, // 19.1.2.8 Object.getOwnPropertySymbols(O) getOwnPropertySymbols: $getOwnPropertySymbols }); // 24.3.2 JSON.stringify(value [, replacer [, space]]) $JSON && $export($export.S + $export.F * (!useNative || buggyJSON), 'JSON', {stringify: $stringify}); // 19.4.3.5 Symbol.prototype[@@toStringTag] setToStringTag($Symbol, 'Symbol'); // 20.2.1.9 Math[@@toStringTag] setToStringTag(Math, 'Math', true); // 24.3.3 JSON[@@toStringTag] setToStringTag(global.JSON, 'JSON', true); },{"./$":52,"./$.an-object":24,"./$.descriptors":30,"./$.enum-keys":32,"./$.export":33,"./$.fails":34,"./$.get-names":36,"./$.global":37,"./$.has":38,"./$.is-array":44,"./$.keyof":53,"./$.library":54,"./$.property-desc":58,"./$.redefine":60,"./$.set-to-string-tag":64,"./$.shared":65,"./$.to-iobject":71,"./$.uid":74,"./$.wks":75}],86:[function(require,module,exports){ require('./es6.array.iterator'); var Iterators = require('./$.iterators'); Iterators.NodeList = Iterators.HTMLCollection = Iterators.Array; },{"./$.iterators":51,"./es6.array.iterator":77}],87:[function(require,module,exports){ // shim for using process in browser var process = module.exports = {}; var queue = []; var draining = false; var currentQueue; var queueIndex = -1; function cleanUpNextTick() { draining = false; if (currentQueue.length) { queue = currentQueue.concat(queue); } else { queueIndex = -1; } if (queue.length) { drainQueue(); } } function drainQueue() { if (draining) { return; } var timeout = setTimeout(cleanUpNextTick); draining = true; var len = queue.length; while(len) { currentQueue = queue; queue = []; while (++queueIndex < len) { if (currentQueue) { currentQueue[queueIndex].run(); } } queueIndex = -1; len = queue.length; } currentQueue = null; draining = false; clearTimeout(timeout); } process.nextTick = function (fun) { var args = new Array(arguments.length - 1); if (arguments.length > 1) { for (var i = 1; i < arguments.length; i++) { args[i - 1] = arguments[i]; } } queue.push(new Item(fun, args)); if (queue.length === 1 && !draining) { setTimeout(drainQueue, 0); } }; // v8 likes predictible objects function Item(fun, array) { this.fun = fun; this.array = array; } Item.prototype.run = function () { this.fun.apply(null, this.array); }; process.title = 'browser'; process.browser = true; process.env = {}; process.argv = []; process.version = ''; // empty string to avoid regexp issues process.versions = {}; function noop() {} process.on = noop; process.addListener = noop; process.once = noop; process.off = noop; process.removeListener = noop; process.removeAllListeners = noop; process.emit = noop; process.binding = function (name) { throw new Error('process.binding is not supported'); }; process.cwd = function () { return '/' }; process.chdir = function (dir) { throw new Error('process.chdir is not supported'); }; process.umask = function() { return 0; }; },{}],88:[function(require,module,exports){ /** * This is the web browser implementation of `debug()`. * * Expose `debug()` as the module. */ exports = module.exports = require('./debug'); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; exports.load = load; exports.useColors = useColors; exports.storage = 'undefined' != typeof chrome && 'undefined' != typeof chrome.storage ? chrome.storage.local : localstorage(); /** * Colors. */ exports.colors = [ 'lightseagreen', 'forestgreen', 'goldenrod', 'dodgerblue', 'darkorchid', 'crimson' ]; /** * Currently only WebKit-based Web Inspectors, Firefox >= v31, * and the Firebug extension (any Firefox version) are known * to support "%c" CSS customizations. * * TODO: add a `localStorage` variable to explicitly enable/disable colors */ function useColors() { // is webkit? http://stackoverflow.com/a/16459606/376773 return ('WebkitAppearance' in document.documentElement.style) || // is firebug? http://stackoverflow.com/a/398120/376773 (window.console && (console.firebug || (console.exception && console.table))) || // is firefox >= v31? // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31); } /** * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. */ exports.formatters.j = function(v) { return JSON.stringify(v); }; /** * Colorize log arguments if enabled. * * @api public */ function formatArgs() { var args = arguments; var useColors = this.useColors; args[0] = (useColors ? '%c' : '') + this.namespace + (useColors ? ' %c' : ' ') + args[0] + (useColors ? '%c ' : ' ') + '+' + exports.humanize(this.diff); if (!useColors) return args; var c = 'color: ' + this.color; args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1)); // the final "%c" is somewhat tricky, because there could be other // arguments passed either before or after the %c, so we need to // figure out the correct index to insert the CSS into var index = 0; var lastC = 0; args[0].replace(/%[a-z%]/g, function(match) { if ('%%' === match) return; index++; if ('%c' === match) { // we only are interested in the *last* %c // (the user may have provided their own) lastC = index; } }); args.splice(lastC, 0, c); return args; } /** * Invokes `console.log()` when available. * No-op when `console.log` is not a "function". * * @api public */ function log() { // this hackery is required for IE8/9, where // the `console.log` function doesn't have 'apply' return 'object' === typeof console && console.log && Function.prototype.apply.call(console.log, console, arguments); } /** * Save `namespaces`. * * @param {String} namespaces * @api private */ function save(namespaces) { try { if (null == namespaces) { exports.storage.removeItem('debug'); } else { exports.storage.debug = namespaces; } } catch(e) {} } /** * Load `namespaces`. * * @return {String} returns the previously persisted debug modes * @api private */ function load() { var r; try { r = exports.storage.debug; } catch(e) {} return r; } /** * Enable namespaces listed in `localStorage.debug` initially. */ exports.enable(load()); /** * Localstorage attempts to return the localstorage. * * This is necessary because safari throws * when a user disables cookies/localstorage * and you attempt to access it. * * @return {LocalStorage} * @api private */ function localstorage(){ try { return window.localStorage; } catch (e) {} } },{"./debug":89}],89:[function(require,module,exports){ /** * This is the common logic for both the Node.js and web browser * implementations of `debug()`. * * Expose `debug()` as the module. */ exports = module.exports = debug; exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; exports.humanize = require('ms'); /** * The currently active debug mode names, and names to skip. */ exports.names = []; exports.skips = []; /** * Map of special "%n" handling functions, for the debug "format" argument. * * Valid key names are a single, lowercased letter, i.e. "n". */ exports.formatters = {}; /** * Previously assigned color. */ var prevColor = 0; /** * Previous log timestamp. */ var prevTime; /** * Select a color. * * @return {Number} * @api private */ function selectColor() { return exports.colors[prevColor++ % exports.colors.length]; } /** * Create a debugger with the given `namespace`. * * @param {String} namespace * @return {Function} * @api public */ function debug(namespace) { // define the `disabled` version function disabled() { } disabled.enabled = false; // define the `enabled` version function enabled() { var self = enabled; // set `diff` timestamp var curr = +new Date(); var ms = curr - (prevTime || curr); self.diff = ms; self.prev = prevTime; self.curr = curr; prevTime = curr; // add the `color` if not set if (null == self.useColors) self.useColors = exports.useColors(); if (null == self.color && self.useColors) self.color = selectColor(); var args = Array.prototype.slice.call(arguments); args[0] = exports.coerce(args[0]); if ('string' !== typeof args[0]) { // anything else let's inspect with %o args = ['%o'].concat(args); } // apply any `formatters` transformations var index = 0; args[0] = args[0].replace(/%([a-z%])/g, function(match, format) { // if we encounter an escaped % then don't increase the array index if (match === '%%') return match; index++; var formatter = exports.formatters[format]; if ('function' === typeof formatter) { var val = args[index]; match = formatter.call(self, val); // now we need to remove `args[index]` since it's inlined in the `format` args.splice(index, 1); index--; } return match; }); if ('function' === typeof exports.formatArgs) { args = exports.formatArgs.apply(self, args); } var logFn = enabled.log || exports.log || console.log.bind(console); logFn.apply(self, args); } enabled.enabled = true; var fn = exports.enabled(namespace) ? enabled : disabled; fn.namespace = namespace; return fn; } /** * Enables a debug mode by namespaces. This can include modes * separated by a colon and wildcards. * * @param {String} namespaces * @api public */ function enable(namespaces) { exports.save(namespaces); var split = (namespaces || '').split(/[\s,]+/); var len = split.length; for (var i = 0; i < len; i++) { if (!split[i]) continue; // ignore empty strings namespaces = split[i].replace(/\*/g, '.*?'); if (namespaces[0] === '-') { exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); } else { exports.names.push(new RegExp('^' + namespaces + '$')); } } } /** * Disable debug output. * * @api public */ function disable() { exports.enable(''); } /** * Returns true if the given mode name is enabled, false otherwise. * * @param {String} name * @return {Boolean} * @api public */ function enabled(name) { var i, len; for (i = 0, len = exports.skips.length; i < len; i++) { if (exports.skips[i].test(name)) { return false; } } for (i = 0, len = exports.names.length; i < len; i++) { if (exports.names[i].test(name)) { return true; } } return false; } /** * Coerce `val`. * * @param {Mixed} val * @return {Mixed} * @api private */ function coerce(val) { if (val instanceof Error) return val.stack || val.message; return val; } },{"ms":90}],90:[function(require,module,exports){ /** * Helpers. */ var s = 1000; var m = s * 60; var h = m * 60; var d = h * 24; var y = d * 365.25; /** * Parse or format the given `val`. * * Options: * * - `long` verbose formatting [false] * * @param {String|Number} val * @param {Object} options * @return {String|Number} * @api public */ module.exports = function(val, options){ options = options || {}; if ('string' == typeof val) return parse(val); return options.long ? long(val) : short(val); }; /** * Parse the given `str` and return milliseconds. * * @param {String} str * @return {Number} * @api private */ function parse(str) { str = '' + str; if (str.length > 10000) return; var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str); if (!match) return; var n = parseFloat(match[1]); var type = (match[2] || 'ms').toLowerCase(); switch (type) { case 'years': case 'year': case 'yrs': case 'yr': case 'y': return n * y; case 'days': case 'day': case 'd': return n * d; case 'hours': case 'hour': case 'hrs': case 'hr': case 'h': return n * h; case 'minutes': case 'minute': case 'mins': case 'min': case 'm': return n * m; case 'seconds': case 'second': case 'secs': case 'sec': case 's': return n * s; case 'milliseconds': case 'millisecond': case 'msecs': case 'msec': case 'ms': return n; } } /** * Short format for `ms`. * * @param {Number} ms * @return {String} * @api private */ function short(ms) { if (ms >= d) return Math.round(ms / d) + 'd'; if (ms >= h) return Math.round(ms / h) + 'h'; if (ms >= m) return Math.round(ms / m) + 'm'; if (ms >= s) return Math.round(ms / s) + 's'; return ms + 'ms'; } /** * Long format for `ms`. * * @param {Number} ms * @return {String} * @api private */ function long(ms) { return plural(ms, d, 'day') || plural(ms, h, 'hour') || plural(ms, m, 'minute') || plural(ms, s, 'second') || ms + ' ms'; } /** * Pluralization helper. */ function plural(ms, n, name) { if (ms < n) return; if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; return Math.ceil(ms / n) + ' ' + name + 's'; } },{}],91:[function(require,module,exports){ 'use strict'; // // We store our EE objects in a plain object whose properties are event names. // If `Object.create(null)` is not supported we prefix the event names with a // `~` to make sure that the built-in object properties are not overridden or // used as an attack vector. // We also assume that `Object.create(null)` is available when the event name // is an ES6 Symbol. // var prefix = typeof Object.create !== 'function' ? '~' : false; /** * Representation of a single EventEmitter function. * * @param {Function} fn Event handler to be called. * @param {Mixed} context Context for function execution. * @param {Boolean} once Only emit once * @api private */ function EE(fn, context, once) { this.fn = fn; this.context = context; this.once = once || false; } /** * Minimal EventEmitter interface that is molded against the Node.js * EventEmitter interface. * * @constructor * @api public */ function EventEmitter() { /* Nothing to set */ } /** * Holds the assigned EventEmitters by name. * * @type {Object} * @private */ EventEmitter.prototype._events = undefined; /** * Return a list of assigned event listeners. * * @param {String} event The events that should be listed. * @param {Boolean} exists We only need to know if there are listeners. * @returns {Array|Boolean} * @api public */ EventEmitter.prototype.listeners = function listeners(event, exists) { var evt = prefix ? prefix + event : event , available = this._events && this._events[evt]; if (exists) return !!available; if (!available) return []; if (available.fn) return [available.fn]; for (var i = 0, l = available.length, ee = new Array(l); i < l; i++) { ee[i] = available[i].fn; } return ee; }; /** * Emit an event to all registered event listeners. * * @param {String} event The name of the event. * @returns {Boolean} Indication if we've emitted an event. * @api public */ EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { var evt = prefix ? prefix + event : event; if (!this._events || !this._events[evt]) return false; var listeners = this._events[evt] , len = arguments.length , args , i; if ('function' === typeof listeners.fn) { if (listeners.once) this.removeListener(event, listeners.fn, undefined, true); switch (len) { case 1: return listeners.fn.call(listeners.context), true; case 2: return listeners.fn.call(listeners.context, a1), true; case 3: return listeners.fn.call(listeners.context, a1, a2), true; case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; } for (i = 1, args = new Array(len -1); i < len; i++) { args[i - 1] = arguments[i]; } listeners.fn.apply(listeners.context, args); } else { var length = listeners.length , j; for (i = 0; i < length; i++) { if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true); switch (len) { case 1: listeners[i].fn.call(listeners[i].context); break; case 2: listeners[i].fn.call(listeners[i].context, a1); break; case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; default: if (!args) for (j = 1, args = new Array(len -1); j < len; j++) { args[j - 1] = arguments[j]; } listeners[i].fn.apply(listeners[i].context, args); } } } return true; }; /** * Register a new EventListener for the given event. * * @param {String} event Name of the event. * @param {Functon} fn Callback function. * @param {Mixed} context The context of the function. * @api public */ EventEmitter.prototype.on = function on(event, fn, context) { var listener = new EE(fn, context || this) , evt = prefix ? prefix + event : event; if (!this._events) this._events = prefix ? {} : Object.create(null); if (!this._events[evt]) this._events[evt] = listener; else { if (!this._events[evt].fn) this._events[evt].push(listener); else this._events[evt] = [ this._events[evt], listener ]; } return this; }; /** * Add an EventListener that's only called once. * * @param {String} event Name of the event. * @param {Function} fn Callback function. * @param {Mixed} context The context of the function. * @api public */ EventEmitter.prototype.once = function once(event, fn, context) { var listener = new EE(fn, context || this, true) , evt = prefix ? prefix + event : event; if (!this._events) this._events = prefix ? {} : Object.create(null); if (!this._events[evt]) this._events[evt] = listener; else { if (!this._events[evt].fn) this._events[evt].push(listener); else this._events[evt] = [ this._events[evt], listener ]; } return this; }; /** * Remove event listeners. * * @param {String} event The event we want to remove. * @param {Function} fn The listener that we need to find. * @param {Mixed} context Only remove listeners matching this context. * @param {Boolean} once Only remove once listeners. * @api public */ EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) { var evt = prefix ? prefix + event : event; if (!this._events || !this._events[evt]) return this; var listeners = this._events[evt] , events = []; if (fn) { if (listeners.fn) { if ( listeners.fn !== fn || (once && !listeners.once) || (context && listeners.context !== context) ) { events.push(listeners); } } else { for (var i = 0, length = listeners.length; i < length; i++) { if ( listeners[i].fn !== fn || (once && !listeners[i].once) || (context && listeners[i].context !== context) ) { events.push(listeners[i]); } } } } // // Reset the array, or remove it completely if we have no more listeners. // if (events.length) { this._events[evt] = events.length === 1 ? events[0] : events; } else { delete this._events[evt]; } return this; }; /** * Remove all listeners or only the listeners for the specified event. * * @param {String} event The event want to remove all listeners for. * @api public */ EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) { if (!this._events) return this; if (event) delete this._events[prefix ? prefix + event : event]; else this._events = prefix ? {} : Object.create(null); return this; }; // // Alias methods names because people roll like that. // EventEmitter.prototype.off = EventEmitter.prototype.removeListener; EventEmitter.prototype.addListener = EventEmitter.prototype.on; // // This function doesn't apply anymore. // EventEmitter.prototype.setMaxListeners = function setMaxListeners() { return this; }; // // Expose the prefix. // EventEmitter.prefixed = prefix; // // Expose the module. // if ('undefined' !== typeof module) { module.exports = EventEmitter; } },{}],92:[function(require,module,exports){ /** * Indicates that navigation was caused by a call to history.push. */ 'use strict'; exports.__esModule = true; var PUSH = 'PUSH'; exports.PUSH = PUSH; /** * Indicates that navigation was caused by a call to history.replace. */ var REPLACE = 'REPLACE'; exports.REPLACE = REPLACE; /** * Indicates that navigation was caused by some other action such * as using a browser's back/forward buttons and/or manually manipulating * the URL in a browser's location bar. This is the default. * * See https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onpopstate * for more information. */ var POP = 'POP'; exports.POP = POP; exports['default'] = { PUSH: PUSH, REPLACE: REPLACE, POP: POP }; },{}],93:[function(require,module,exports){ "use strict"; exports.__esModule = true; exports.loopAsync = loopAsync; function loopAsync(turns, work, callback) { var currentTurn = 0; var isDone = false; function done() { isDone = true; callback.apply(this, arguments); } function next() { if (isDone) return; if (currentTurn < turns) { work.call(this, currentTurn++, next, done); } else { done.apply(this, arguments); } } next(); } },{}],94:[function(require,module,exports){ (function (process){ /*eslint-disable no-empty */ 'use strict'; exports.__esModule = true; exports.saveState = saveState; exports.readState = readState; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _warning = require('warning'); var _warning2 = _interopRequireDefault(_warning); var KeyPrefix = '@@History/'; var QuotaExceededError = 'QuotaExceededError'; var SecurityError = 'SecurityError'; function createKey(key) { return KeyPrefix + key; } function saveState(key, state) { try { window.sessionStorage.setItem(createKey(key), JSON.stringify(state)); } catch (error) { if (error.name === SecurityError) { // Blocking cookies in Chrome/Firefox/Safari throws SecurityError on any // attempt to access window.sessionStorage. process.env.NODE_ENV !== 'production' ? _warning2['default'](false, '[history] Unable to save state; sessionStorage is not available due to security settings') : undefined; return; } if (error.name === QuotaExceededError && window.sessionStorage.length === 0) { // Safari "private mode" throws QuotaExceededError. process.env.NODE_ENV !== 'production' ? _warning2['default'](false, '[history] Unable to save state; sessionStorage is not available in Safari private mode') : undefined; return; } throw error; } } function readState(key) { var json = undefined; try { json = window.sessionStorage.getItem(createKey(key)); } catch (error) { if (error.name === SecurityError) { // Blocking cookies in Chrome/Firefox/Safari throws SecurityError on any // attempt to access window.sessionStorage. process.env.NODE_ENV !== 'production' ? _warning2['default'](false, '[history] Unable to read state; sessionStorage is not available due to security settings') : undefined; return null; } } if (json) { try { return JSON.parse(json); } catch (error) { // Ignore invalid JSON. } } return null; } }).call(this,require('_process')) },{"_process":87,"warning":117}],95:[function(require,module,exports){ 'use strict'; exports.__esModule = true; exports.addEventListener = addEventListener; exports.removeEventListener = removeEventListener; exports.getHashPath = getHashPath; exports.replaceHashPath = replaceHashPath; exports.getWindowPath = getWindowPath; exports.go = go; exports.getUserConfirmation = getUserConfirmation; exports.supportsHistory = supportsHistory; exports.supportsGoWithoutReloadUsingHash = supportsGoWithoutReloadUsingHash; function addEventListener(node, event, listener) { if (node.addEventListener) { node.addEventListener(event, listener, false); } else { node.attachEvent('on' + event, listener); } } function removeEventListener(node, event, listener) { if (node.removeEventListener) { node.removeEventListener(event, listener, false); } else { node.detachEvent('on' + event, listener); } } function getHashPath() { // We can't use window.location.hash here because it's not // consistent across browsers - Firefox will pre-decode it! return window.location.href.split('#')[1] || ''; } function replaceHashPath(path) { window.location.replace(window.location.pathname + window.location.search + '#' + path); } function getWindowPath() { return window.location.pathname + window.location.search + window.location.hash; } function go(n) { if (n) window.history.go(n); } function getUserConfirmation(message, callback) { callback(window.confirm(message)); } /** * Returns true if the HTML5 history API is supported. Taken from Modernizr. * * https://github.com/Modernizr/Modernizr/blob/master/LICENSE * https://github.com/Modernizr/Modernizr/blob/master/feature-detects/history.js * changed to avoid false negatives for Windows Phones: https://github.com/rackt/react-router/issues/586 */ function supportsHistory() { var ua = navigator.userAgent; if ((ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) && ua.indexOf('Mobile Safari') !== -1 && ua.indexOf('Chrome') === -1 && ua.indexOf('Windows Phone') === -1) { return false; } // FIXME: Work around our browser history not working correctly on Chrome // iOS: https://github.com/rackt/react-router/issues/2565 if (ua.indexOf('CriOS') !== -1) { return false; } return window.history && 'pushState' in window.history; } /** * Returns false if using go(n) with hash history causes a full page reload. */ function supportsGoWithoutReloadUsingHash() { var ua = navigator.userAgent; return ua.indexOf('Firefox') === -1; } },{}],96:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement); exports.canUseDOM = canUseDOM; },{}],97:[function(require,module,exports){ (function (process){ 'use strict'; exports.__esModule = true; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _invariant = require('invariant'); var _invariant2 = _interopRequireDefault(_invariant); var _Actions = require('./Actions'); var _ExecutionEnvironment = require('./ExecutionEnvironment'); var _DOMUtils = require('./DOMUtils'); var _DOMStateStorage = require('./DOMStateStorage'); var _createDOMHistory = require('./createDOMHistory'); var _createDOMHistory2 = _interopRequireDefault(_createDOMHistory); var _parsePath = require('./parsePath'); var _parsePath2 = _interopRequireDefault(_parsePath); /** * Creates and returns a history object that uses HTML5's history API * (pushState, replaceState, and the popstate event) to manage history. * This is the recommended method of managing history in browsers because * it provides the cleanest URLs. * * Note: In browsers that do not support the HTML5 history API full * page reloads will be used to preserve URLs. */ function createBrowserHistory() { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; !_ExecutionEnvironment.canUseDOM ? process.env.NODE_ENV !== 'production' ? _invariant2['default'](false, 'Browser history needs a DOM') : _invariant2['default'](false) : undefined; var forceRefresh = options.forceRefresh; var isSupported = _DOMUtils.supportsHistory(); var useRefresh = !isSupported || forceRefresh; function getCurrentLocation(historyState) { historyState = historyState || window.history.state || {}; var path = _DOMUtils.getWindowPath(); var _historyState = historyState; var key = _historyState.key; var state = undefined; if (key) { state = _DOMStateStorage.readState(key); } else { state = null; key = history.createKey(); if (isSupported) window.history.replaceState(_extends({}, historyState, { key: key }), null, path); } var location = _parsePath2['default'](path); return history.createLocation(_extends({}, location, { state: state }), undefined, key); } function startPopStateListener(_ref) { var transitionTo = _ref.transitionTo; function popStateListener(event) { if (event.state === undefined) return; // Ignore extraneous popstate events in WebKit. transitionTo(getCurrentLocation(event.state)); } _DOMUtils.addEventListener(window, 'popstate', popStateListener); return function () { _DOMUtils.removeEventListener(window, 'popstate', popStateListener); }; } function finishTransition(location) { var basename = location.basename; var pathname = location.pathname; var search = location.search; var hash = location.hash; var state = location.state; var action = location.action; var key = location.key; if (action === _Actions.POP) return; // Nothing to do. _DOMStateStorage.saveState(key, state); var path = (basename || '') + pathname + search + hash; var historyState = { key: key }; if (action === _Actions.PUSH) { if (useRefresh) { window.location.href = path; return false; // Prevent location update. } else { window.history.pushState(historyState, null, path); } } else { // REPLACE if (useRefresh) { window.location.replace(path); return false; // Prevent location update. } else { window.history.replaceState(historyState, null, path); } } } var history = _createDOMHistory2['default'](_extends({}, options, { getCurrentLocation: getCurrentLocation, finishTransition: finishTransition, saveState: _DOMStateStorage.saveState })); var listenerCount = 0, stopPopStateListener = undefined; function listenBefore(listener) { if (++listenerCount === 1) stopPopStateListener = startPopStateListener(history); var unlisten = history.listenBefore(listener); return function () { unlisten(); if (--listenerCount === 0) stopPopStateListener(); }; } function listen(listener) { if (++listenerCount === 1) stopPopStateListener = startPopStateListener(history); var unlisten = history.listen(listener); return function () { unlisten(); if (--listenerCount === 0) stopPopStateListener(); }; } // deprecated function registerTransitionHook(hook) { if (++listenerCount === 1) stopPopStateListener = startPopStateListener(history); history.registerTransitionHook(hook); } // deprecated function unregisterTransitionHook(hook) { history.unregisterTransitionHook(hook); if (--listenerCount === 0) stopPopStateListener(); } return _extends({}, history, { listenBefore: listenBefore, listen: listen, registerTransitionHook: registerTransitionHook, unregisterTransitionHook: unregisterTransitionHook }); } exports['default'] = createBrowserHistory; module.exports = exports['default']; }).call(this,require('_process')) },{"./Actions":92,"./DOMStateStorage":94,"./DOMUtils":95,"./ExecutionEnvironment":96,"./createDOMHistory":98,"./parsePath":108,"_process":87,"invariant":116}],98:[function(require,module,exports){ (function (process){ 'use strict'; exports.__esModule = true; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _invariant = require('invariant'); var _invariant2 = _interopRequireDefault(_invariant); var _ExecutionEnvironment = require('./ExecutionEnvironment'); var _DOMUtils = require('./DOMUtils'); var _createHistory = require('./createHistory'); var _createHistory2 = _interopRequireDefault(_createHistory); function createDOMHistory(options) { var history = _createHistory2['default'](_extends({ getUserConfirmation: _DOMUtils.getUserConfirmation }, options, { go: _DOMUtils.go })); function listen(listener) { !_ExecutionEnvironment.canUseDOM ? process.env.NODE_ENV !== 'production' ? _invariant2['default'](false, 'DOM history needs a DOM') : _invariant2['default'](false) : undefined; return history.listen(listener); } return _extends({}, history, { listen: listen }); } exports['default'] = createDOMHistory; module.exports = exports['default']; }).call(this,require('_process')) },{"./DOMUtils":95,"./ExecutionEnvironment":96,"./createHistory":100,"_process":87,"invariant":116}],99:[function(require,module,exports){ (function (process){ 'use strict'; exports.__esModule = true; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _warning = require('warning'); var _warning2 = _interopRequireDefault(_warning); var _invariant = require('invariant'); var _invariant2 = _interopRequireDefault(_invariant); var _Actions = require('./Actions'); var _ExecutionEnvironment = require('./ExecutionEnvironment'); var _DOMUtils = require('./DOMUtils'); var _DOMStateStorage = require('./DOMStateStorage'); var _createDOMHistory = require('./createDOMHistory'); var _createDOMHistory2 = _interopRequireDefault(_createDOMHistory); var _parsePath = require('./parsePath'); var _parsePath2 = _interopRequireDefault(_parsePath); function isAbsolutePath(path) { return typeof path === 'string' && path.charAt(0) === '/'; } function ensureSlash() { var path = _DOMUtils.getHashPath(); if (isAbsolutePath(path)) return true; _DOMUtils.replaceHashPath('/' + path); return false; } function addQueryStringValueToPath(path, key, value) { return path + (path.indexOf('?') === -1 ? '?' : '&') + (key + '=' + value); } function stripQueryStringValueFromPath(path, key) { return path.replace(new RegExp('[?&]?' + key + '=[a-zA-Z0-9]+'), ''); } function getQueryStringValueFromPath(path, key) { var match = path.match(new RegExp('\\?.*?\\b' + key + '=(.+?)\\b')); return match && match[1]; } var DefaultQueryKey = '_k'; function createHashHistory() { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; !_ExecutionEnvironment.canUseDOM ? process.env.NODE_ENV !== 'production' ? _invariant2['default'](false, 'Hash history needs a DOM') : _invariant2['default'](false) : undefined; var queryKey = options.queryKey; if (queryKey === undefined || !!queryKey) queryKey = typeof queryKey === 'string' ? queryKey : DefaultQueryKey; function getCurrentLocation() { var path = _DOMUtils.getHashPath(); var key = undefined, state = undefined; if (queryKey) { key = getQueryStringValueFromPath(path, queryKey); path = stripQueryStringValueFromPath(path, queryKey); if (key) { state = _DOMStateStorage.readState(key); } else { state = null; key = history.createKey(); _DOMUtils.replaceHashPath(addQueryStringValueToPath(path, queryKey, key)); } } else { key = state = null; } var location = _parsePath2['default'](path); return history.createLocation(_extends({}, location, { state: state }), undefined, key); } function startHashChangeListener(_ref) { var transitionTo = _ref.transitionTo; function hashChangeListener() { if (!ensureSlash()) return; // Always make sure hashes are preceeded with a /. transitionTo(getCurrentLocation()); } ensureSlash(); _DOMUtils.addEventListener(window, 'hashchange', hashChangeListener); return function () { _DOMUtils.removeEventListener(window, 'hashchange', hashChangeListener); }; } function finishTransition(location) { var basename = location.basename; var pathname = location.pathname; var search = location.search; var state = location.state; var action = location.action; var key = location.key; if (action === _Actions.POP) return; // Nothing to do. var path = (basename || '') + pathname + search; if (queryKey) { path = addQueryStringValueToPath(path, queryKey, key); _DOMStateStorage.saveState(key, state); } else { // Drop key and state. location.key = location.state = null; } var currentHash = _DOMUtils.getHashPath(); if (action === _Actions.PUSH) { if (currentHash !== path) { window.location.hash = path; } else { process.env.NODE_ENV !== 'production' ? _warning2['default'](false, 'You cannot PUSH the same path using hash history') : undefined; } } else if (currentHash !== path) { // REPLACE _DOMUtils.replaceHashPath(path); } } var history = _createDOMHistory2['default'](_extends({}, options, { getCurrentLocation: getCurrentLocation, finishTransition: finishTransition, saveState: _DOMStateStorage.saveState })); var listenerCount = 0, stopHashChangeListener = undefined; function listenBefore(listener) { if (++listenerCount === 1) stopHashChangeListener = startHashChangeListener(history); var unlisten = history.listenBefore(listener); return function () { unlisten(); if (--listenerCount === 0) stopHashChangeListener(); }; } function listen(listener) { if (++listenerCount === 1) stopHashChangeListener = startHashChangeListener(history); var unlisten = history.listen(listener); return function () { unlisten(); if (--listenerCount === 0) stopHashChangeListener(); }; } function push(location) { process.env.NODE_ENV !== 'production' ? _warning2['default'](queryKey || location.state == null, 'You cannot use state without a queryKey it will be dropped') : undefined; history.push(location); } function replace(location) { process.env.NODE_ENV !== 'production' ? _warning2['default'](queryKey || location.state == null, 'You cannot use state without a queryKey it will be dropped') : undefined; history.replace(location); } var goIsSupportedWithoutReload = _DOMUtils.supportsGoWithoutReloadUsingHash(); function go(n) { process.env.NODE_ENV !== 'production' ? _warning2['default'](goIsSupportedWithoutReload, 'Hash history go(n) causes a full page reload in this browser') : undefined; history.go(n); } function createHref(path) { return '#' + history.createHref(path); } // deprecated function registerTransitionHook(hook) { if (++listenerCount === 1) stopHashChangeListener = startHashChangeListener(history); history.registerTransitionHook(hook); } // deprecated function unregisterTransitionHook(hook) { history.unregisterTransitionHook(hook); if (--listenerCount === 0) stopHashChangeListener(); } // deprecated function pushState(state, path) { process.env.NODE_ENV !== 'production' ? _warning2['default'](queryKey || state == null, 'You cannot use state without a queryKey it will be dropped') : undefined; history.pushState(state, path); } // deprecated function replaceState(state, path) { process.env.NODE_ENV !== 'production' ? _warning2['default'](queryKey || state == null, 'You cannot use state without a queryKey it will be dropped') : undefined; history.replaceState(state, path); } return _extends({}, history, { listenBefore: listenBefore, listen: listen, push: push, replace: replace, go: go, createHref: createHref, registerTransitionHook: registerTransitionHook, // deprecated - warning is in createHistory unregisterTransitionHook: unregisterTransitionHook, // deprecated - warning is in createHistory pushState: pushState, // deprecated - warning is in createHistory replaceState: replaceState // deprecated - warning is in createHistory }); } exports['default'] = createHashHistory; module.exports = exports['default']; }).call(this,require('_process')) },{"./Actions":92,"./DOMStateStorage":94,"./DOMUtils":95,"./ExecutionEnvironment":96,"./createDOMHistory":98,"./parsePath":108,"_process":87,"invariant":116,"warning":117}],100:[function(require,module,exports){ //import warning from 'warning' 'use strict'; exports.__esModule = true; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _deepEqual = require('deep-equal'); var _deepEqual2 = _interopRequireDefault(_deepEqual); var _AsyncUtils = require('./AsyncUtils'); var _Actions = require('./Actions'); var _createLocation2 = require('./createLocation'); var _createLocation3 = _interopRequireDefault(_createLocation2); var _runTransitionHook = require('./runTransitionHook'); var _runTransitionHook2 = _interopRequireDefault(_runTransitionHook); var _parsePath = require('./parsePath'); var _parsePath2 = _interopRequireDefault(_parsePath); var _deprecate = require('./deprecate'); var _deprecate2 = _interopRequireDefault(_deprecate); function createRandomKey(length) { return Math.random().toString(36).substr(2, length); } function locationsAreEqual(a, b) { return a.pathname === b.pathname && a.search === b.search && //a.action === b.action && // Different action !== location change. a.key === b.key && _deepEqual2['default'](a.state, b.state); } var DefaultKeyLength = 6; function createHistory() { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; var getCurrentLocation = options.getCurrentLocation; var finishTransition = options.finishTransition; var saveState = options.saveState; var go = options.go; var keyLength = options.keyLength; var getUserConfirmation = options.getUserConfirmation; if (typeof keyLength !== 'number') keyLength = DefaultKeyLength; var transitionHooks = []; function listenBefore(hook) { transitionHooks.push(hook); return function () { transitionHooks = transitionHooks.filter(function (item) { return item !== hook; }); }; } var allKeys = []; var changeListeners = []; var location = undefined; function getCurrent() { if (pendingLocation && pendingLocation.action === _Actions.POP) { return allKeys.indexOf(pendingLocation.key); } else if (location) { return allKeys.indexOf(location.key); } else { return -1; } } function updateLocation(newLocation) { var current = getCurrent(); location = newLocation; if (location.action === _Actions.PUSH) { allKeys = [].concat(allKeys.slice(0, current + 1), [location.key]); } else if (location.action === _Actions.REPLACE) { allKeys[current] = location.key; } changeListeners.forEach(function (listener) { listener(location); }); } function listen(listener) { changeListeners.push(listener); if (location) { listener(location); } else { var _location = getCurrentLocation(); allKeys = [_location.key]; updateLocation(_location); } return function () { changeListeners = changeListeners.filter(function (item) { return item !== listener; }); }; } function confirmTransitionTo(location, callback) { _AsyncUtils.loopAsync(transitionHooks.length, function (index, next, done) { _runTransitionHook2['default'](transitionHooks[index], location, function (result) { if (result != null) { done(result); } else { next(); } }); }, function (message) { if (getUserConfirmation && typeof message === 'string') { getUserConfirmation(message, function (ok) { callback(ok !== false); }); } else { callback(message !== false); } }); } var pendingLocation = undefined; function transitionTo(nextLocation) { if (location && locationsAreEqual(location, nextLocation)) return; // Nothing to do. pendingLocation = nextLocation; confirmTransitionTo(nextLocation, function (ok) { if (pendingLocation !== nextLocation) return; // Transition was interrupted. if (ok) { // treat PUSH to current path like REPLACE to be consistent with browsers if (nextLocation.action === _Actions.PUSH) { var prevPath = createPath(location); var nextPath = createPath(nextLocation); if (nextPath === prevPath) nextLocation.action = _Actions.REPLACE; } if (finishTransition(nextLocation) !== false) updateLocation(nextLocation); } else if (location && nextLocation.action === _Actions.POP) { var prevIndex = allKeys.indexOf(location.key); var nextIndex = allKeys.indexOf(nextLocation.key); if (prevIndex !== -1 && nextIndex !== -1) go(prevIndex - nextIndex); // Restore the URL. } }); } function push(location) { transitionTo(createLocation(location, _Actions.PUSH, createKey())); } function replace(location) { transitionTo(createLocation(location, _Actions.REPLACE, createKey())); } function goBack() { go(-1); } function goForward() { go(1); } function createKey() { return createRandomKey(keyLength); } function createPath(location) { if (location == null || typeof location === 'string') return location; var pathname = location.pathname; var search = location.search; var hash = location.hash; var result = pathname; if (search) result += search; if (hash) result += hash; return result; } function createHref(location) { return createPath(location); } function createLocation(location, action) { var key = arguments.length <= 2 || arguments[2] === undefined ? createKey() : arguments[2]; if (typeof action === 'object') { //warning( // false, // 'The state (2nd) argument to history.createLocation is deprecated; use a ' + // 'location descriptor instead' //) if (typeof location === 'string') location = _parsePath2['default'](location); location = _extends({}, location, { state: action }); action = key; key = arguments[3] || createKey(); } return _createLocation3['default'](location, action, key); } // deprecated function setState(state) { if (location) { updateLocationState(location, state); updateLocation(location); } else { updateLocationState(getCurrentLocation(), state); } } function updateLocationState(location, state) { location.state = _extends({}, location.state, state); saveState(location.key, location.state); } // deprecated function registerTransitionHook(hook) { if (transitionHooks.indexOf(hook) === -1) transitionHooks.push(hook); } // deprecated function unregisterTransitionHook(hook) { transitionHooks = transitionHooks.filter(function (item) { return item !== hook; }); } // deprecated function pushState(state, path) { if (typeof path === 'string') path = _parsePath2['default'](path); push(_extends({ state: state }, path)); } // deprecated function replaceState(state, path) { if (typeof path === 'string') path = _parsePath2['default'](path); replace(_extends({ state: state }, path)); } return { listenBefore: listenBefore, listen: listen, transitionTo: transitionTo, push: push, replace: replace, go: go, goBack: goBack, goForward: goForward, createKey: createKey, createPath: createPath, createHref: createHref, createLocation: createLocation, setState: _deprecate2['default'](setState, 'setState is deprecated; use location.key to save state instead'), registerTransitionHook: _deprecate2['default'](registerTransitionHook, 'registerTransitionHook is deprecated; use listenBefore instead'), unregisterTransitionHook: _deprecate2['default'](unregisterTransitionHook, 'unregisterTransitionHook is deprecated; use the callback returned from listenBefore instead'), pushState: _deprecate2['default'](pushState, 'pushState is deprecated; use push instead'), replaceState: _deprecate2['default'](replaceState, 'replaceState is deprecated; use replace instead') }; } exports['default'] = createHistory; module.exports = exports['default']; },{"./Actions":92,"./AsyncUtils":93,"./createLocation":101,"./deprecate":103,"./parsePath":108,"./runTransitionHook":109,"deep-equal":113}],101:[function(require,module,exports){ //import warning from 'warning' 'use strict'; exports.__esModule = true; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _Actions = require('./Actions'); var _parsePath = require('./parsePath'); var _parsePath2 = _interopRequireDefault(_parsePath); function createLocation() { var location = arguments.length <= 0 || arguments[0] === undefined ? '/' : arguments[0]; var action = arguments.length <= 1 || arguments[1] === undefined ? _Actions.POP : arguments[1]; var key = arguments.length <= 2 || arguments[2] === undefined ? null : arguments[2]; var _fourthArg = arguments.length <= 3 || arguments[3] === undefined ? null : arguments[3]; if (typeof location === 'string') location = _parsePath2['default'](location); if (typeof action === 'object') { //warning( // false, // 'The state (2nd) argument to createLocation is deprecated; use a ' + // 'location descriptor instead' //) location = _extends({}, location, { state: action }); action = key || _Actions.POP; key = _fourthArg; } var pathname = location.pathname || '/'; var search = location.search || ''; var hash = location.hash || ''; var state = location.state || null; return { pathname: pathname, search: search, hash: hash, state: state, action: action, key: key }; } exports['default'] = createLocation; module.exports = exports['default']; },{"./Actions":92,"./parsePath":108}],102:[function(require,module,exports){ (function (process){ 'use strict'; exports.__esModule = true; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _warning = require('warning'); var _warning2 = _interopRequireDefault(_warning); var _invariant = require('invariant'); var _invariant2 = _interopRequireDefault(_invariant); var _Actions = require('./Actions'); var _createHistory = require('./createHistory'); var _createHistory2 = _interopRequireDefault(_createHistory); var _parsePath = require('./parsePath'); var _parsePath2 = _interopRequireDefault(_parsePath); function createStateStorage(entries) { return entries.filter(function (entry) { return entry.state; }).reduce(function (memo, entry) { memo[entry.key] = entry.state; return memo; }, {}); } function createMemoryHistory() { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; if (Array.isArray(options)) { options = { entries: options }; } else if (typeof options === 'string') { options = { entries: [options] }; } var history = _createHistory2['default'](_extends({}, options, { getCurrentLocation: getCurrentLocation, finishTransition: finishTransition, saveState: saveState, go: go })); var _options = options; var entries = _options.entries; var current = _options.current; if (typeof entries === 'string') { entries = [entries]; } else if (!Array.isArray(entries)) { entries = ['/']; } entries = entries.map(function (entry) { var key = history.createKey(); if (typeof entry === 'string') return { pathname: entry, key: key }; if (typeof entry === 'object' && entry) return _extends({}, entry, { key: key }); !false ? process.env.NODE_ENV !== 'production' ? _invariant2['default'](false, 'Unable to create history entry from %s', entry) : _invariant2['default'](false) : undefined; }); if (current == null) { current = entries.length - 1; } else { !(current >= 0 && current < entries.length) ? process.env.NODE_ENV !== 'production' ? _invariant2['default'](false, 'Current index must be >= 0 and < %s, was %s', entries.length, current) : _invariant2['default'](false) : undefined; } var storage = createStateStorage(entries); function saveState(key, state) { storage[key] = state; } function readState(key) { return storage[key]; } function getCurrentLocation() { var entry = entries[current]; var key = entry.key; var basename = entry.basename; var pathname = entry.pathname; var search = entry.search; var path = (basename || '') + pathname + (search || ''); var state = undefined; if (key) { state = readState(key); } else { state = null; key = history.createKey(); entry.key = key; } var location = _parsePath2['default'](path); return history.createLocation(_extends({}, location, { state: state }), undefined, key); } function canGo(n) { var index = current + n; return index >= 0 && index < entries.length; } function go(n) { if (n) { if (!canGo(n)) { process.env.NODE_ENV !== 'production' ? _warning2['default'](false, 'Cannot go(%s) there is not enough history', n) : undefined; return; } current += n; var currentLocation = getCurrentLocation(); // change action to POP history.transitionTo(_extends({}, currentLocation, { action: _Actions.POP })); } } function finishTransition(location) { switch (location.action) { case _Actions.PUSH: current += 1; // if we are not on the top of stack // remove rest and push new if (current < entries.length) entries.splice(current); entries.push(location); saveState(location.key, location.state); break; case _Actions.REPLACE: entries[current] = location; saveState(location.key, location.state); break; } } return history; } exports['default'] = createMemoryHistory; module.exports = exports['default']; }).call(this,require('_process')) },{"./Actions":92,"./createHistory":100,"./parsePath":108,"_process":87,"invariant":116,"warning":117}],103:[function(require,module,exports){ //import warning from 'warning' "use strict"; exports.__esModule = true; function deprecate(fn) { return fn; //return function () { // warning(false, '[history] ' + message) // return fn.apply(this, arguments) //} } exports["default"] = deprecate; module.exports = exports["default"]; },{}],104:[function(require,module,exports){ 'use strict'; exports.__esModule = true; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _deprecate = require('./deprecate'); var _deprecate2 = _interopRequireDefault(_deprecate); var _useBeforeUnload = require('./useBeforeUnload'); var _useBeforeUnload2 = _interopRequireDefault(_useBeforeUnload); exports['default'] = _deprecate2['default'](_useBeforeUnload2['default'], 'enableBeforeUnload is deprecated, use useBeforeUnload instead'); module.exports = exports['default']; },{"./deprecate":103,"./useBeforeUnload":111}],105:[function(require,module,exports){ 'use strict'; exports.__esModule = true; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _deprecate = require('./deprecate'); var _deprecate2 = _interopRequireDefault(_deprecate); var _useQueries = require('./useQueries'); var _useQueries2 = _interopRequireDefault(_useQueries); exports['default'] = _deprecate2['default'](_useQueries2['default'], 'enableQueries is deprecated, use useQueries instead'); module.exports = exports['default']; },{"./deprecate":103,"./useQueries":112}],106:[function(require,module,exports){ "use strict"; exports.__esModule = true; function extractPath(string) { var match = string.match(/^https?:\/\/[^\/]*/); if (match == null) return string; return string.substring(match[0].length); } exports["default"] = extractPath; module.exports = exports["default"]; },{}],107:[function(require,module,exports){ 'use strict'; exports.__esModule = true; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _deprecate = require('./deprecate'); var _deprecate2 = _interopRequireDefault(_deprecate); var _createLocation2 = require('./createLocation'); var _createLocation3 = _interopRequireDefault(_createLocation2); var _createBrowserHistory = require('./createBrowserHistory'); var _createBrowserHistory2 = _interopRequireDefault(_createBrowserHistory); exports.createHistory = _createBrowserHistory2['default']; var _createHashHistory2 = require('./createHashHistory'); var _createHashHistory3 = _interopRequireDefault(_createHashHistory2); exports.createHashHistory = _createHashHistory3['default']; var _createMemoryHistory2 = require('./createMemoryHistory'); var _createMemoryHistory3 = _interopRequireDefault(_createMemoryHistory2); exports.createMemoryHistory = _createMemoryHistory3['default']; var _useBasename2 = require('./useBasename'); var _useBasename3 = _interopRequireDefault(_useBasename2); exports.useBasename = _useBasename3['default']; var _useBeforeUnload2 = require('./useBeforeUnload'); var _useBeforeUnload3 = _interopRequireDefault(_useBeforeUnload2); exports.useBeforeUnload = _useBeforeUnload3['default']; var _useQueries2 = require('./useQueries'); var _useQueries3 = _interopRequireDefault(_useQueries2); exports.useQueries = _useQueries3['default']; var _Actions2 = require('./Actions'); var _Actions3 = _interopRequireDefault(_Actions2); exports.Actions = _Actions3['default']; // deprecated var _enableBeforeUnload2 = require('./enableBeforeUnload'); var _enableBeforeUnload3 = _interopRequireDefault(_enableBeforeUnload2); exports.enableBeforeUnload = _enableBeforeUnload3['default']; var _enableQueries2 = require('./enableQueries'); var _enableQueries3 = _interopRequireDefault(_enableQueries2); exports.enableQueries = _enableQueries3['default']; var createLocation = _deprecate2['default'](_createLocation3['default'], 'Using createLocation without a history instance is deprecated; please use history.createLocation instead'); exports.createLocation = createLocation; },{"./Actions":92,"./createBrowserHistory":97,"./createHashHistory":99,"./createLocation":101,"./createMemoryHistory":102,"./deprecate":103,"./enableBeforeUnload":104,"./enableQueries":105,"./useBasename":110,"./useBeforeUnload":111,"./useQueries":112}],108:[function(require,module,exports){ (function (process){ 'use strict'; exports.__esModule = true; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _warning = require('warning'); var _warning2 = _interopRequireDefault(_warning); var _extractPath = require('./extractPath'); var _extractPath2 = _interopRequireDefault(_extractPath); function parsePath(path) { var pathname = _extractPath2['default'](path); var search = ''; var hash = ''; process.env.NODE_ENV !== 'production' ? _warning2['default'](path === pathname, 'A path must be pathname + search + hash only, not a fully qualified URL like "%s"', path) : undefined; var hashIndex = pathname.indexOf('#'); if (hashIndex !== -1) { hash = pathname.substring(hashIndex); pathname = pathname.substring(0, hashIndex); } var searchIndex = pathname.indexOf('?'); if (searchIndex !== -1) { search = pathname.substring(searchIndex); pathname = pathname.substring(0, searchIndex); } if (pathname === '') pathname = '/'; return { pathname: pathname, search: search, hash: hash }; } exports['default'] = parsePath; module.exports = exports['default']; }).call(this,require('_process')) },{"./extractPath":106,"_process":87,"warning":117}],109:[function(require,module,exports){ (function (process){ 'use strict'; exports.__esModule = true; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _warning = require('warning'); var _warning2 = _interopRequireDefault(_warning); function runTransitionHook(hook, location, callback) { var result = hook(location, callback); if (hook.length < 2) { // Assume the hook runs synchronously and automatically // call the callback with the return value. callback(result); } else { process.env.NODE_ENV !== 'production' ? _warning2['default'](result === undefined, 'You should not "return" in a transition hook with a callback argument; call the callback instead') : undefined; } } exports['default'] = runTransitionHook; module.exports = exports['default']; }).call(this,require('_process')) },{"_process":87,"warning":117}],110:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } var _ExecutionEnvironment = require('./ExecutionEnvironment'); var _runTransitionHook = require('./runTransitionHook'); var _runTransitionHook2 = _interopRequireDefault(_runTransitionHook); var _extractPath = require('./extractPath'); var _extractPath2 = _interopRequireDefault(_extractPath); var _parsePath = require('./parsePath'); var _parsePath2 = _interopRequireDefault(_parsePath); var _deprecate = require('./deprecate'); var _deprecate2 = _interopRequireDefault(_deprecate); function useBasename(createHistory) { return function () { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; var basename = options.basename; var historyOptions = _objectWithoutProperties(options, ['basename']); var history = createHistory(historyOptions); // Automatically use the value of in HTML // documents as basename if it's not explicitly given. if (basename == null && _ExecutionEnvironment.canUseDOM) { var base = document.getElementsByTagName('base')[0]; if (base) basename = _extractPath2['default'](base.href); } function addBasename(location) { if (basename && location.basename == null) { if (location.pathname.indexOf(basename) === 0) { location.pathname = location.pathname.substring(basename.length); location.basename = basename; if (location.pathname === '') location.pathname = '/'; } else { location.basename = ''; } } return location; } function prependBasename(location) { if (!basename) return location; if (typeof location === 'string') location = _parsePath2['default'](location); var pname = location.pathname; var normalizedBasename = basename.slice(-1) === '/' ? basename : basename + '/'; var normalizedPathname = pname.charAt(0) === '/' ? pname.slice(1) : pname; var pathname = normalizedBasename + normalizedPathname; return _extends({}, location, { pathname: pathname }); } // Override all read methods with basename-aware versions. function listenBefore(hook) { return history.listenBefore(function (location, callback) { _runTransitionHook2['default'](hook, addBasename(location), callback); }); } function listen(listener) { return history.listen(function (location) { listener(addBasename(location)); }); } // Override all write methods with basename-aware versions. function push(location) { history.push(prependBasename(location)); } function replace(location) { history.replace(prependBasename(location)); } function createPath(location) { return history.createPath(prependBasename(location)); } function createHref(location) { return history.createHref(prependBasename(location)); } function createLocation() { return addBasename(history.createLocation.apply(history, arguments)); } // deprecated function pushState(state, path) { if (typeof path === 'string') path = _parsePath2['default'](path); push(_extends({ state: state }, path)); } // deprecated function replaceState(state, path) { if (typeof path === 'string') path = _parsePath2['default'](path); replace(_extends({ state: state }, path)); } return _extends({}, history, { listenBefore: listenBefore, listen: listen, push: push, replace: replace, createPath: createPath, createHref: createHref, createLocation: createLocation, pushState: _deprecate2['default'](pushState, 'pushState is deprecated; use push instead'), replaceState: _deprecate2['default'](replaceState, 'replaceState is deprecated; use replace instead') }); }; } exports['default'] = useBasename; module.exports = exports['default']; },{"./ExecutionEnvironment":96,"./deprecate":103,"./extractPath":106,"./parsePath":108,"./runTransitionHook":109}],111:[function(require,module,exports){ (function (process){ 'use strict'; exports.__esModule = true; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _warning = require('warning'); var _warning2 = _interopRequireDefault(_warning); var _ExecutionEnvironment = require('./ExecutionEnvironment'); var _DOMUtils = require('./DOMUtils'); var _deprecate = require('./deprecate'); var _deprecate2 = _interopRequireDefault(_deprecate); function startBeforeUnloadListener(getBeforeUnloadPromptMessage) { function listener(event) { var message = getBeforeUnloadPromptMessage(); if (typeof message === 'string') { (event || window.event).returnValue = message; return message; } } _DOMUtils.addEventListener(window, 'beforeunload', listener); return function () { _DOMUtils.removeEventListener(window, 'beforeunload', listener); }; } /** * Returns a new createHistory function that can be used to create * history objects that know how to use the beforeunload event in web * browsers to cancel navigation. */ function useBeforeUnload(createHistory) { return function (options) { var history = createHistory(options); var stopBeforeUnloadListener = undefined; var beforeUnloadHooks = []; function getBeforeUnloadPromptMessage() { var message = undefined; for (var i = 0, len = beforeUnloadHooks.length; message == null && i < len; ++i) { message = beforeUnloadHooks[i].call(); }return message; } function listenBeforeUnload(hook) { beforeUnloadHooks.push(hook); if (beforeUnloadHooks.length === 1) { if (_ExecutionEnvironment.canUseDOM) { stopBeforeUnloadListener = startBeforeUnloadListener(getBeforeUnloadPromptMessage); } else { process.env.NODE_ENV !== 'production' ? _warning2['default'](false, 'listenBeforeUnload only works in DOM environments') : undefined; } } return function () { beforeUnloadHooks = beforeUnloadHooks.filter(function (item) { return item !== hook; }); if (beforeUnloadHooks.length === 0 && stopBeforeUnloadListener) { stopBeforeUnloadListener(); stopBeforeUnloadListener = null; } }; } // deprecated function registerBeforeUnloadHook(hook) { if (_ExecutionEnvironment.canUseDOM && beforeUnloadHooks.indexOf(hook) === -1) { beforeUnloadHooks.push(hook); if (beforeUnloadHooks.length === 1) stopBeforeUnloadListener = startBeforeUnloadListener(getBeforeUnloadPromptMessage); } } // deprecated function unregisterBeforeUnloadHook(hook) { if (beforeUnloadHooks.length > 0) { beforeUnloadHooks = beforeUnloadHooks.filter(function (item) { return item !== hook; }); if (beforeUnloadHooks.length === 0) stopBeforeUnloadListener(); } } return _extends({}, history, { listenBeforeUnload: listenBeforeUnload, registerBeforeUnloadHook: _deprecate2['default'](registerBeforeUnloadHook, 'registerBeforeUnloadHook is deprecated; use listenBeforeUnload instead'), unregisterBeforeUnloadHook: _deprecate2['default'](unregisterBeforeUnloadHook, 'unregisterBeforeUnloadHook is deprecated; use the callback returned from listenBeforeUnload instead') }); }; } exports['default'] = useBeforeUnload; module.exports = exports['default']; }).call(this,require('_process')) },{"./DOMUtils":95,"./ExecutionEnvironment":96,"./deprecate":103,"_process":87,"warning":117}],112:[function(require,module,exports){ (function (process){ 'use strict'; exports.__esModule = true; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } var _warning = require('warning'); var _warning2 = _interopRequireDefault(_warning); var _queryString = require('query-string'); var _runTransitionHook = require('./runTransitionHook'); var _runTransitionHook2 = _interopRequireDefault(_runTransitionHook); var _parsePath = require('./parsePath'); var _parsePath2 = _interopRequireDefault(_parsePath); var _deprecate = require('./deprecate'); var _deprecate2 = _interopRequireDefault(_deprecate); var SEARCH_BASE_KEY = '$searchBase'; function defaultStringifyQuery(query) { return _queryString.stringify(query).replace(/%20/g, '+'); } var defaultParseQueryString = _queryString.parse; function isNestedObject(object) { for (var p in object) { if (object.hasOwnProperty(p) && typeof object[p] === 'object' && !Array.isArray(object[p]) && object[p] !== null) return true; }return false; } /** * Returns a new createHistory function that may be used to create * history objects that know how to handle URL queries. */ function useQueries(createHistory) { return function () { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; var stringifyQuery = options.stringifyQuery; var parseQueryString = options.parseQueryString; var historyOptions = _objectWithoutProperties(options, ['stringifyQuery', 'parseQueryString']); var history = createHistory(historyOptions); if (typeof stringifyQuery !== 'function') stringifyQuery = defaultStringifyQuery; if (typeof parseQueryString !== 'function') parseQueryString = defaultParseQueryString; function addQuery(location) { if (location.query == null) { var search = location.search; location.query = parseQueryString(search.substring(1)); location[SEARCH_BASE_KEY] = { search: search, searchBase: '' }; } // TODO: Instead of all the book-keeping here, this should just strip the // stringified query from the search. return location; } function appendQuery(location, query) { var _extends2; var queryString = undefined; if (!query || (queryString = stringifyQuery(query)) === '') return location; process.env.NODE_ENV !== 'production' ? _warning2['default'](stringifyQuery !== defaultStringifyQuery || !isNestedObject(query), 'useQueries does not stringify nested query objects by default; ' + 'use a custom stringifyQuery function') : undefined; if (typeof location === 'string') location = _parsePath2['default'](location); var searchBaseSpec = location[SEARCH_BASE_KEY]; var searchBase = undefined; if (searchBaseSpec && location.search === searchBaseSpec.search) { searchBase = searchBaseSpec.searchBase; } else { searchBase = location.search || ''; } var search = searchBase + (searchBase ? '&' : '?') + queryString; return _extends({}, location, (_extends2 = { search: search }, _extends2[SEARCH_BASE_KEY] = { search: search, searchBase: searchBase }, _extends2)); } // Override all read methods with query-aware versions. function listenBefore(hook) { return history.listenBefore(function (location, callback) { _runTransitionHook2['default'](hook, addQuery(location), callback); }); } function listen(listener) { return history.listen(function (location) { listener(addQuery(location)); }); } // Override all write methods with query-aware versions. function push(location) { history.push(appendQuery(location, location.query)); } function replace(location) { history.replace(appendQuery(location, location.query)); } function createPath(location, query) { //warning( // !query, // 'the query argument to createPath is deprecated; use a location descriptor instead' //) return history.createPath(appendQuery(location, query || location.query)); } function createHref(location, query) { //warning( // !query, // 'the query argument to createHref is deprecated; use a location descriptor instead' //) return history.createHref(appendQuery(location, query || location.query)); } function createLocation() { return addQuery(history.createLocation.apply(history, arguments)); } // deprecated function pushState(state, path, query) { if (typeof path === 'string') path = _parsePath2['default'](path); push(_extends({ state: state }, path, { query: query })); } // deprecated function replaceState(state, path, query) { if (typeof path === 'string') path = _parsePath2['default'](path); replace(_extends({ state: state }, path, { query: query })); } return _extends({}, history, { listenBefore: listenBefore, listen: listen, push: push, replace: replace, createPath: createPath, createHref: createHref, createLocation: createLocation, pushState: _deprecate2['default'](pushState, 'pushState is deprecated; use push instead'), replaceState: _deprecate2['default'](replaceState, 'replaceState is deprecated; use replace instead') }); }; } exports['default'] = useQueries; module.exports = exports['default']; }).call(this,require('_process')) },{"./deprecate":103,"./parsePath":108,"./runTransitionHook":109,"_process":87,"query-string":120,"warning":117}],113:[function(require,module,exports){ var pSlice = Array.prototype.slice; var objectKeys = require('./lib/keys.js'); var isArguments = require('./lib/is_arguments.js'); var deepEqual = module.exports = function (actual, expected, opts) { if (!opts) opts = {}; // 7.1. All identical values are equivalent, as determined by ===. if (actual === expected) { return true; } else if (actual instanceof Date && expected instanceof Date) { return actual.getTime() === expected.getTime(); // 7.3. Other pairs that do not both pass typeof value == 'object', // equivalence is determined by ==. } else if (!actual || !expected || typeof actual != 'object' && typeof expected != 'object') { return opts.strict ? actual === expected : actual == expected; // 7.4. For all other Object pairs, including Array objects, equivalence is // determined by having the same number of owned properties (as verified // with Object.prototype.hasOwnProperty.call), the same set of keys // (although not necessarily the same order), equivalent values for every // corresponding key, and an identical 'prototype' property. Note: this // accounts for both named and indexed properties on Arrays. } else { return objEquiv(actual, expected, opts); } } function isUndefinedOrNull(value) { return value === null || value === undefined; } function isBuffer (x) { if (!x || typeof x !== 'object' || typeof x.length !== 'number') return false; if (typeof x.copy !== 'function' || typeof x.slice !== 'function') { return false; } if (x.length > 0 && typeof x[0] !== 'number') return false; return true; } function objEquiv(a, b, opts) { var i, key; if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) return false; // an identical 'prototype' property. if (a.prototype !== b.prototype) return false; //~~~I've managed to break Object.keys through screwy arguments passing. // Converting to array solves the problem. if (isArguments(a)) { if (!isArguments(b)) { return false; } a = pSlice.call(a); b = pSlice.call(b); return deepEqual(a, b, opts); } if (isBuffer(a)) { if (!isBuffer(b)) { return false; } if (a.length !== b.length) return false; for (i = 0; i < a.length; i++) { if (a[i] !== b[i]) return false; } return true; } try { var ka = objectKeys(a), kb = objectKeys(b); } catch (e) {//happens when one is a string literal and the other isn't return false; } // having the same number of owned properties (keys incorporates // hasOwnProperty) if (ka.length != kb.length) return false; //the same set of keys (although not necessarily the same order), ka.sort(); kb.sort(); //~~~cheap key test for (i = ka.length - 1; i >= 0; i--) { if (ka[i] != kb[i]) return false; } //equivalent values for every corresponding key, and //~~~possibly expensive deep test for (i = ka.length - 1; i >= 0; i--) { key = ka[i]; if (!deepEqual(a[key], b[key], opts)) return false; } return typeof a === typeof b; } },{"./lib/is_arguments.js":114,"./lib/keys.js":115}],114:[function(require,module,exports){ var supportsArgumentsClass = (function(){ return Object.prototype.toString.call(arguments) })() == '[object Arguments]'; exports = module.exports = supportsArgumentsClass ? supported : unsupported; exports.supported = supported; function supported(object) { return Object.prototype.toString.call(object) == '[object Arguments]'; }; exports.unsupported = unsupported; function unsupported(object){ return object && typeof object == 'object' && typeof object.length == 'number' && Object.prototype.hasOwnProperty.call(object, 'callee') && !Object.prototype.propertyIsEnumerable.call(object, 'callee') || false; }; },{}],115:[function(require,module,exports){ exports = module.exports = typeof Object.keys === 'function' ? Object.keys : shim; exports.shim = shim; function shim (obj) { var keys = []; for (var key in obj) keys.push(key); return keys; } },{}],116:[function(require,module,exports){ (function (process){ /** * Copyright 2013-2015, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ 'use strict'; /** * Use invariant() to assert state which your program assumes to be true. * * Provide sprintf-style format (only %s is supported) and arguments * to provide information about what broke and what you were * expecting. * * The invariant message will be stripped in production, but the invariant * will remain to ensure logic does not differ in production. */ var invariant = function(condition, format, a, b, c, d, e, f) { if (process.env.NODE_ENV !== 'production') { if (format === undefined) { throw new Error('invariant requires an error message argument'); } } if (!condition) { var error; if (format === undefined) { error = new Error( 'Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.' ); } else { var args = [a, b, c, d, e, f]; var argIndex = 0; error = new Error( format.replace(/%s/g, function() { return args[argIndex++]; }) ); error.name = 'Invariant Violation'; } error.framesToPop = 1; // we don't care about invariant's own frame throw error; } }; module.exports = invariant; }).call(this,require('_process')) },{"_process":87}],117:[function(require,module,exports){ (function (process){ /** * Copyright 2014-2015, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ 'use strict'; /** * Similar to invariant but only logs a warning if the condition is not met. * This can be used to log issues in development environments in critical * paths. Removing the logging code for production environments will keep the * same logic and follow the same code paths. */ var warning = function() {}; if (process.env.NODE_ENV !== 'production') { warning = function(condition, format, args) { var len = arguments.length; args = new Array(len > 2 ? len - 2 : 0); for (var key = 2; key < len; key++) { args[key - 2] = arguments[key]; } if (format === undefined) { throw new Error( '`warning(condition, format, ...args)` requires a warning ' + 'message argument' ); } if (format.length < 10 || (/^[s\W]*$/).test(format)) { throw new Error( 'The warning format should be able to uniquely identify this ' + 'warning. Please, use a more descriptive format than: ' + format ); } if (!condition) { var argIndex = 0; var message = 'Warning: ' + format.replace(/%s/g, function() { return args[argIndex++]; }); if (typeof console !== 'undefined') { console.error(message); } try { // This error was thrown as a convenience so that you can use this stack // to find the callsite that caused this warning to fire. throw new Error(message); } catch(x) {} } }; } module.exports = warning; }).call(this,require('_process')) },{"_process":87}],118:[function(require,module,exports){ var isarray = require('isarray') /** * Expose `pathToRegexp`. */ module.exports = pathToRegexp module.exports.parse = parse module.exports.compile = compile module.exports.tokensToFunction = tokensToFunction module.exports.tokensToRegExp = tokensToRegExp /** * The main path matching regexp utility. * * @type {RegExp} */ var PATH_REGEXP = new RegExp([ // Match escaped characters that would otherwise appear in future matches. // This allows the user to escape special characters that won't transform. '(\\\\.)', // Match Express-style parameters and un-named parameters with a prefix // and optional suffixes. Matches appear as: // // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined] // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined] // "/*" => ["/", undefined, undefined, undefined, undefined, "*"] '([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^()])+)\\))?|\\(((?:\\\\.|[^()])+)\\))([+*?])?|(\\*))' ].join('|'), 'g') /** * Parse a string for the raw tokens. * * @param {String} str * @return {Array} */ function parse (str) { var tokens = [] var key = 0 var index = 0 var path = '' var res while ((res = PATH_REGEXP.exec(str)) != null) { var m = res[0] var escaped = res[1] var offset = res.index path += str.slice(index, offset) index = offset + m.length // Ignore already escaped sequences. if (escaped) { path += escaped[1] continue } // Push the current path onto the tokens. if (path) { tokens.push(path) path = '' } var prefix = res[2] var name = res[3] var capture = res[4] var group = res[5] var suffix = res[6] var asterisk = res[7] var repeat = suffix === '+' || suffix === '*' var optional = suffix === '?' || suffix === '*' var delimiter = prefix || '/' var pattern = capture || group || (asterisk ? '.*' : '[^' + delimiter + ']+?') tokens.push({ name: name || key++, prefix: prefix || '', delimiter: delimiter, optional: optional, repeat: repeat, pattern: escapeGroup(pattern) }) } // Match any characters still remaining. if (index < str.length) { path += str.substr(index) } // If the path exists, push it onto the end. if (path) { tokens.push(path) } return tokens } /** * Compile a string to a template function for the path. * * @param {String} str * @return {Function} */ function compile (str) { return tokensToFunction(parse(str)) } /** * Expose a method for transforming tokens into the path function. */ function tokensToFunction (tokens) { // Compile all the tokens into regexps. var matches = new Array(tokens.length) // Compile all the patterns before compilation. for (var i = 0; i < tokens.length; i++) { if (typeof tokens[i] === 'object') { matches[i] = new RegExp('^' + tokens[i].pattern + '$') } } return function (obj) { var path = '' var data = obj || {} for (var i = 0; i < tokens.length; i++) { var token = tokens[i] if (typeof token === 'string') { path += token continue } var value = data[token.name] var segment if (value == null) { if (token.optional) { continue } else { throw new TypeError('Expected "' + token.name + '" to be defined') } } if (isarray(value)) { if (!token.repeat) { throw new TypeError('Expected "' + token.name + '" to not repeat, but received "' + value + '"') } if (value.length === 0) { if (token.optional) { continue } else { throw new TypeError('Expected "' + token.name + '" to not be empty') } } for (var j = 0; j < value.length; j++) { segment = encodeURIComponent(value[j]) if (!matches[i].test(segment)) { throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"') } path += (j === 0 ? token.prefix : token.delimiter) + segment } continue } segment = encodeURIComponent(value) if (!matches[i].test(segment)) { throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"') } path += token.prefix + segment } return path } } /** * Escape a regular expression string. * * @param {String} str * @return {String} */ function escapeString (str) { return str.replace(/([.+*?=^!:${}()[\]|\/])/g, '\\$1') } /** * Escape the capturing group by escaping special characters and meaning. * * @param {String} group * @return {String} */ function escapeGroup (group) { return group.replace(/([=!:$\/()])/g, '\\$1') } /** * Attach the keys as a property of the regexp. * * @param {RegExp} re * @param {Array} keys * @return {RegExp} */ function attachKeys (re, keys) { re.keys = keys return re } /** * Get the flags for a regexp from the options. * * @param {Object} options * @return {String} */ function flags (options) { return options.sensitive ? '' : 'i' } /** * Pull out keys from a regexp. * * @param {RegExp} path * @param {Array} keys * @return {RegExp} */ function regexpToRegexp (path, keys) { // Use a negative lookahead to match only capturing groups. var groups = path.source.match(/\((?!\?)/g) if (groups) { for (var i = 0; i < groups.length; i++) { keys.push({ name: i, prefix: null, delimiter: null, optional: false, repeat: false, pattern: null }) } } return attachKeys(path, keys) } /** * Transform an array into a regexp. * * @param {Array} path * @param {Array} keys * @param {Object} options * @return {RegExp} */ function arrayToRegexp (path, keys, options) { var parts = [] for (var i = 0; i < path.length; i++) { parts.push(pathToRegexp(path[i], keys, options).source) } var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options)) return attachKeys(regexp, keys) } /** * Create a path regexp from string input. * * @param {String} path * @param {Array} keys * @param {Object} options * @return {RegExp} */ function stringToRegexp (path, keys, options) { var tokens = parse(path) var re = tokensToRegExp(tokens, options) // Attach keys back to the regexp. for (var i = 0; i < tokens.length; i++) { if (typeof tokens[i] !== 'string') { keys.push(tokens[i]) } } return attachKeys(re, keys) } /** * Expose a function for taking tokens and returning a RegExp. * * @param {Array} tokens * @param {Array} keys * @param {Object} options * @return {RegExp} */ function tokensToRegExp (tokens, options) { options = options || {} var strict = options.strict var end = options.end !== false var route = '' var lastToken = tokens[tokens.length - 1] var endsWithSlash = typeof lastToken === 'string' && /\/$/.test(lastToken) // Iterate over the tokens and create our regexp string. for (var i = 0; i < tokens.length; i++) { var token = tokens[i] if (typeof token === 'string') { route += escapeString(token) } else { var prefix = escapeString(token.prefix) var capture = token.pattern if (token.repeat) { capture += '(?:' + prefix + capture + ')*' } if (token.optional) { if (prefix) { capture = '(?:' + prefix + '(' + capture + '))?' } else { capture = '(' + capture + ')?' } } else { capture = prefix + '(' + capture + ')' } route += capture } } // In non-strict mode we allow a slash at the end of match. If the path to // match already ends with a slash, we remove it for consistency. The slash // is valid at the end of a path match, not in the middle. This is important // in non-ending mode, where "/test/" shouldn't match "/test//route". if (!strict) { route = (endsWithSlash ? route.slice(0, -2) : route) + '(?:\\/(?=$))?' } if (end) { route += '$' } else { // In non-ending mode, we need the capturing groups to match as much as // possible by using a positive lookahead to the end or next path segment. route += strict && endsWithSlash ? '' : '(?=\\/|$)' } return new RegExp('^' + route, flags(options)) } /** * Normalize the given path string, returning a regular expression. * * An empty array can be passed in for the keys, which will hold the * placeholder key descriptions. For example, using `/user/:id`, `keys` will * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`. * * @param {(String|RegExp|Array)} path * @param {Array} [keys] * @param {Object} [options] * @return {RegExp} */ function pathToRegexp (path, keys, options) { keys = keys || [] if (!isarray(keys)) { options = keys keys = [] } else if (!options) { options = {} } if (path instanceof RegExp) { return regexpToRegexp(path, keys, options) } if (isarray(path)) { return arrayToRegexp(path, keys, options) } return stringToRegexp(path, keys, options) } },{"isarray":119}],119:[function(require,module,exports){ module.exports = Array.isArray || function (arr) { return Object.prototype.toString.call(arr) == '[object Array]'; }; },{}],120:[function(require,module,exports){ 'use strict'; var strictUriEncode = require('strict-uri-encode'); exports.extract = function (str) { return str.split('?')[1] || ''; }; exports.parse = function (str) { if (typeof str !== 'string') { return {}; } str = str.trim().replace(/^(\?|#|&)/, ''); if (!str) { return {}; } return str.split('&').reduce(function (ret, param) { var parts = param.replace(/\+/g, ' ').split('='); // Firefox (pre 40) decodes `%3D` to `=` // https://github.com/sindresorhus/query-string/pull/37 var key = parts.shift(); var val = parts.length > 0 ? parts.join('=') : undefined; key = decodeURIComponent(key); // missing `=` should be `null`: // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters val = val === undefined ? null : decodeURIComponent(val); if (!ret.hasOwnProperty(key)) { ret[key] = val; } else if (Array.isArray(ret[key])) { ret[key].push(val); } else { ret[key] = [ret[key], val]; } return ret; }, {}); }; exports.stringify = function (obj) { return obj ? Object.keys(obj).sort().map(function (key) { var val = obj[key]; if (val === undefined) { return ''; } if (val === null) { return key; } if (Array.isArray(val)) { return val.sort().map(function (val2) { return strictUriEncode(key) + '=' + strictUriEncode(val2); }).join('&'); } return strictUriEncode(key) + '=' + strictUriEncode(val); }).filter(function (x) { return x.length > 0; }).join('&') : ''; }; },{"strict-uri-encode":121}],121:[function(require,module,exports){ 'use strict'; module.exports = function (str) { return encodeURIComponent(str).replace(/[!'()*]/g, function (c) { return '%' + c.charCodeAt(0).toString(16); }); }; },{}],122:[function(require,module,exports){ 'use strict'; var _keys = require('babel-runtime/core-js/object/keys'); var _keys2 = _interopRequireDefault(_keys); var _assign = require('babel-runtime/core-js/object/assign'); var _assign2 = _interopRequireDefault(_assign); var _vue = require('vue'); var _vue2 = _interopRequireDefault(_vue); var _debug = require('debug'); var _debug2 = _interopRequireDefault(_debug); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var debug = (0, _debug2.default)('voie:directive'); _vue2.default.elementDirective('v-view', { bind: function bind() { var _vm$$options = this.vm.$options; var state = _vm$$options.state; var manager = _vm$$options.manager; manager.mountPoints[state.name] = { hostVm: this.vm, viewEl: this.el, viewElChildren: [].slice.call(this.el.children) }; debug('registered v-view', this.el); }, unbind: function unbind() { var _vm$$options2 = this.vm.$options; var state = _vm$$options2.state; var manager = _vm$$options2.manager; delete manager.mountPoints[state.name]; debug('unregistered v-view', this.el); } }); _vue2.default.directive('link', { bind: function bind() { this.manager = resolveManager(this.vm); if (!this.manager) { throw new Error('Can\'t locate state manager.'); } this.manager.on('context_updated', this.updateElement, this); }, unbind: function unbind() { this.manager.off('context_updated', this.updateElement, this); }, update: function update(value) { var _this = this; if (!value) { throw new Error('v-link: expression "' + this.expression + '" should resolve to { name: ..., params... }}'); } var manager = this.manager; var name = null; if (typeof value == 'string') { name = value; this.params = {}; } else { name = value.name; this.params = value.params || {}; } this.state = manager.get(name); if (!this.state) { /* eslint-disable no-console */ console.warn('State "' + name + '" not found.'); /* eslint-enable no-console */ return; } this.el.onclick = function (ev) { ev.preventDefault(); manager.go({ name: name, params: _this.params }); }; this.updateElement(); }, updateElement: function updateElement() { var manager = this.manager; var ctx = manager.context; var state = this.state; if (!state) { return; } var params = (0, _assign2.default)({}, ctx.params, this.params); this.el.setAttribute('href', state.createHref(params)); // Add/remove active class this.el.classList.remove(manager.activeClass); if (ctx.state) { var paramsMatch = (0, _keys2.default)(params).every(function (key) { return ctx.params[key] === params[key]; }); var active = ctx.state.includes(state) && paramsMatch; if (active) { this.el.classList.add(manager.activeClass); } else { this.el.classList.remove(manager.activeClass); } } } }); function resolveManager(vm) { var manager = vm.$options.manager; if (manager) { return manager; } if (vm.$parent) { return resolveManager(vm.$parent); } return null; } },{"babel-runtime/core-js/object/assign":1,"babel-runtime/core-js/object/keys":5,"debug":88,"vue":"vue"}],123:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.RedirectLoopError = exports.StateNotFoundError = undefined; var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); var _inherits2 = require('babel-runtime/helpers/inherits'); var _inherits3 = _interopRequireDefault(_inherits2); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var StateNotFoundError = exports.StateNotFoundError = (function (_Error) { (0, _inherits3.default)(StateNotFoundError, _Error); function StateNotFoundError(name) { (0, _classCallCheck3.default)(this, StateNotFoundError); var _this = (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(StateNotFoundError).call(this, 'State "' + name + '" not found.')); _this.name = name; return _this; } return StateNotFoundError; })(Error); var RedirectLoopError = exports.RedirectLoopError = (function (_Error2) { (0, _inherits3.default)(RedirectLoopError, _Error2); function RedirectLoopError(transition) { (0, _classCallCheck3.default)(this, RedirectLoopError); var _this2 = (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(RedirectLoopError).call(this, 'Redirect loop detected ' + '(set maxRedirects on state manager to configure max redirects per transition)')); _this2.transition = transition; return _this2; } return RedirectLoopError; })(Error); },{"babel-runtime/core-js/object/get-prototype-of":4,"babel-runtime/helpers/classCallCheck":9,"babel-runtime/helpers/inherits":11,"babel-runtime/helpers/possibleConstructorReturn":12}],124:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.Transition = exports.State = exports.StateManager = undefined; var _stateManager = require('./state-manager'); var _stateManager2 = _interopRequireDefault(_stateManager); var _state = require('./state'); var _state2 = _interopRequireDefault(_state); var _transition = require('./transition'); var _transition2 = _interopRequireDefault(_transition); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } exports.StateManager = _stateManager2.default; exports.State = _state2.default; exports.Transition = _transition2.default; },{"./state":126,"./state-manager":125,"./transition":127}],125:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _keys = require('babel-runtime/core-js/object/keys'); var _keys2 = _interopRequireDefault(_keys); var _assign = require('babel-runtime/core-js/object/assign'); var _assign2 = _interopRequireDefault(_assign); var _typeof2 = require('babel-runtime/helpers/typeof'); var _typeof3 = _interopRequireDefault(_typeof2); var _promise = require('babel-runtime/core-js/promise'); var _promise2 = _interopRequireDefault(_promise); var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _createClass2 = require('babel-runtime/helpers/createClass'); var _createClass3 = _interopRequireDefault(_createClass2); var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); var _inherits2 = require('babel-runtime/helpers/inherits'); var _inherits3 = _interopRequireDefault(_inherits2); var _debug = require('debug'); var _debug2 = _interopRequireDefault(_debug); var _eventemitter = require('eventemitter3'); var _eventemitter2 = _interopRequireDefault(_eventemitter); var _state = require('./state'); var _state2 = _interopRequireDefault(_state); var _transition = require('./transition'); var _transition2 = _interopRequireDefault(_transition); var _history = require('history'); require('./directives'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var debug = (0, _debug2.default)('voie:manager'); /** * State manager holds the hierarchy of application states, * exposes methods for navigating around states and * keeps track of current state and its `context` * (object `{ state, params, data, ... }`). * * A typical application will have a single instance * of state manager exposed as a module. * * ```es6 * import { StateManager } from 'voie'; * * export default new StateManager({ ... }); * ``` * * State manager emits following events: * * * `history_updated` * * `context_updated` * * `transition_finished` * */ var StateManager = (function (_EventEmitter) { (0, _inherits3.default)(StateManager, _EventEmitter); /** * Instantiates new state manager. * * Options: * * * `el` — required, root DOM element for rendering views * (can be either `HTMLElement` or selector string) * * * history — a history object (see `rackt/history`) * * * base — (only used when `history` is not specified), base href * for application (URL pathname prefix) * * * `maxRedirects` — maximum number of redirects within * a single transition, when exceeded transition will fail with * `RedirectLoopError` (default is 10) * * * `activeClass` — active class for `v-link` directive * (default is "active")' * * * `handleUncaught` — `function(err) => Promise` invoked * when transition fails with error * * * `beforeEach` — `function(ctx) => Promise` invoked * before each `enter` hook */ function StateManager(spec) { (0, _classCallCheck3.default)(this, StateManager); var _this = (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(StateManager).call(this)); _this._setupEl(spec); _this._setupHistory(spec); _this._setupOptions(spec); _this._setupHooks(spec); _this._setupState(); return _this; } (0, _createClass3.default)(StateManager, [{ key: '_setupEl', value: function _setupEl(spec) { this.el = spec.el instanceof HTMLElement ? spec.el : document.querySelector(spec.el); if (!this.el) { throw new Error('Please specify `el` as an entry-point node of your app.'); } } }, { key: '_setupHistory', value: function _setupHistory(spec) { if (spec.history) { this.history = spec.history; } else { this._setupDefaultHtml5History(spec); } } }, { key: '_setupDefaultHtml5History', value: function _setupDefaultHtml5History(spec) { var base = spec.base; // Try to take base from `` if (!base) { var baseEl = document.querySelector('base'); base = baseEl && baseEl.getAttribute('href'); } base = (base || '').replace(/\/+$/, ''); this.history = (0, _history.useBasename)(_history.createHistory)({ basename: base }); } }, { key: '_setupOptions', value: function _setupOptions(spec) { if (spec.handleUncaught) { this.handleUncaught = spec.handleUncaught; } this.maxRedirects = Number(spec.maxRedirects) || 10; this.activeClass = spec.activeClass || 'active'; } }, { key: '_setupHooks', value: function _setupHooks(spec) { if (spec.beforeEach) { this.beforeEach = spec.beforeEach; } if (spec.afterEach) { this.afterEach = spec.afterEach; } } }, { key: '_setupState', value: function _setupState() { this.states = {}; this.context = { parent: null, state: null, // root state vm: null, params: {}, data: {} }; this.mountPoints = { '': { viewEl: this.el, viewElChildren: [].slice.call(this.el.children) } }; this.transition = null; } /** * Executed before `enter` hooks on each state. * * @returns {Promise} */ }, { key: 'beforeEach', value: function beforeEach() {} /** * Executed after `leave` hooks on each state. * * @returns {Promise} */ }, { key: 'afterEach', value: function afterEach() {} /** * Handles errors uncaught during transition. * Used for overriding on a per-instance basis. * * @returns {Promise} */ }, { key: 'handleUncaught', value: function handleUncaught(err) { return _promise2.default.reject(err); } /** * Registers a new state with specified `name`. * * You can use one of two styles: * * ```es6 * sm.add('foo', { ... }); * sm.add({ name: 'foo', ... }); * ``` * * All options are passed to `State` constructor. * * @returns {State} */ }, { key: 'add', value: function add(name, spec) { if ((typeof name === 'undefined' ? 'undefined' : (0, _typeof3.default)(name)) == 'object') { spec = name; name = spec.name; } if (!name) { throw new Error('State `name` is mandatory.'); } if (this.states[name]) { throw new Error('State "' + name + '" already added'); } spec.name = name; var state = new _state2.default(this, spec); debug('add %s', name); this.states[name] = state; return state; } /** * Retrieves a state previously registered via `add`. * * @returns {State} */ }, { key: 'get', value: function get(name) { return this.states[name]; } /** * Navigates to a state with specified `name`. * Navigation is performed asynchronously, allowing * state enter/leave hook to perform async tasks * (e.g. fetch data). * * Throws an exception if another transition is * taking place. * * Options: * * * `name` — target state name (if null, will transition to same state * with updated params) * * `params` — object containing state parameters * (either path variables or query string parameters) * * `replace` — if `true` don't create a separate record * in browser history (default is `false`) * * Transition process: * * * find nearest common ancestor state with matching parameters * * go "upstream", leaving states, cleaning up states, * destroying components * * go "downstream", entering states, instantiating and rendering * components * * update browser history to reflect target state * (e.g. set new URL in address bar) * * Note that it is the responsibility of the state to compute * target URL. * * @returns {Promise} resolved when navigation is finished. */ }, { key: 'go', value: function go(options) { var _this2 = this; if (this.transition) { throw new Error('Transition is in progress.'); } this.transition = new _transition2.default(this); var currentState = this.context.state; var name = null; if (typeof options === 'string') { name = options; options = {}; } else if (options.name) { name = options.name; } else if (currentState) { name = currentState.name; } else { throw new Error('Destination state not specified'); } return _promise2.default.resolve().then(function () { return _this2.transition.go(name, options.params || {}); }).then(function (result) { _this2.transition = null; _this2.emit('transition_finished'); return result; }).catch(function (err) { _this2.transition = null; _this2.emit('transition_finished', err); return _this2.handleUncaught(err); }).then(function () { return _this2._updateHistory(options.replace || false); }); } /** * Updates browser history without actually performing * any transitions. * * Typically used to serialize parameters into query string * without performing navigation (e.g. when params are used * only in Vue components). */ }, { key: 'update', value: function update(params, replace) { var _this3 = this; (0, _assign2.default)(this.context.params, params); this.emit('context_updated', this.context); return _promise2.default.resolve().then(function () { return _this3._updateHistory(replace); }); } /** * Resolves nearest mount point in current context tree. * * Mount point is a "slot" that corresponds to `v-view` directive * (and the component that hosts it). */ }, { key: '_getMountPoint', value: function _getMountPoint() { var ctx = this.context; var el = null; while (ctx && !el) { var state = ctx.state; if (state) { el = this.mountPoints[state.name]; } else { el = this.mountPoints['']; } ctx = ctx.parent; } return el; } /** * Begins listening for history events (e.g. browser back button) * and performs initial navigation by matching current URL. * * @returns {Promise} resolved when initial navigation is finished. */ }, { key: 'start', value: function start() { var _this4 = this; if (this._unlisten) { return _promise2.default.resolve(); } this._unlisten = this.history.listen(function (location) { return _this4._matchLocation(location); }); return new _promise2.default(function (resolve) { return _this4.once('history_updated', resolve); }); } /** * Stops listening for history events. */ }, { key: 'stop', value: function stop() { if (!this._unlisten) { return; } this._unlisten(); delete this._unlisten; } }, { key: '_matchLocation', value: function _matchLocation(location) { var _this5 = this; var url = location.pathname + location.search; if (url === this.context.url) { return; } var found = (0, _keys2.default)(this.states).find(function (name) { var state = _this5.states[name]; var matched = state._match(location); if (matched) { debug('match url %s -> %s', location.pathname, name); _this5.go({ name: name, params: matched, replace: true }); return true; } }); if (!found) { /* eslint-disable no-console */ console.warn('No states match URL: ' + location.pathname); /* eslint-enable no-console */ this._updateHistory(true); } } }, { key: '_updateHistory', value: function _updateHistory(replace) { var state = this.context.state; var url = state ? state._makeUrl(this.context.params) : '/'; if (url === this.context.url) { return; } this.context.url = url; if (replace) { this.history.replace(url); } else { this.history.push(url); } this.emit('history_updated', { url: url, ctx: this.context }); } }]); return StateManager; })(_eventemitter2.default); exports.default = StateManager; ; },{"./directives":122,"./state":126,"./transition":127,"babel-runtime/core-js/object/assign":1,"babel-runtime/core-js/object/get-prototype-of":4,"babel-runtime/core-js/object/keys":5,"babel-runtime/core-js/promise":7,"babel-runtime/helpers/classCallCheck":9,"babel-runtime/helpers/createClass":10,"babel-runtime/helpers/inherits":11,"babel-runtime/helpers/possibleConstructorReturn":12,"babel-runtime/helpers/typeof":13,"debug":88,"eventemitter3":91,"history":107}],126:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _keys = require('babel-runtime/core-js/object/keys'); var _keys2 = _interopRequireDefault(_keys); var _promise = require('babel-runtime/core-js/promise'); var _promise2 = _interopRequireDefault(_promise); var _assign = require('babel-runtime/core-js/object/assign'); var _assign2 = _interopRequireDefault(_assign); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _createClass2 = require('babel-runtime/helpers/createClass'); var _createClass3 = _interopRequireDefault(_createClass2); var _pathToRegexp = require('path-to-regexp'); var _pathToRegexp2 = _interopRequireDefault(_pathToRegexp); var _queryString = require('query-string'); var _queryString2 = _interopRequireDefault(_queryString); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * Represents a node in application states hierarchy. * * Most state features are optional, the only required * setting is a unique `name` and a reference to `parent` state. * * Depending on features a state can represent: * * * an abstract view (e.g. layout containing a component with `` * directive) * * a concrete view * * a route with path pattern and query parameters * * a data resolver that provides data to its descendants * * a mix of above * * Invoking constructor directly is not recommended, * use `StateManager#add` instead. * * @private */ var State = (function () { /** * Options: * * * `name` — required, state name; if contains dots `.` * then parent state will be inferred from this name * (e.g. `layout.users` will become a parent of `layout.users.list`) * unless `parent` is specified explicitly * * * `parent` — optional, parent state (this option disables * parent-from-name inference) * * * `path` — optional pathname pattern for matching URLs and * extracting parameters (e.g. `/user/:userId`). If started with `/`, * then path is treated as an absolute pathname, otherwise * path is treated as a fragment (absolute pathname is inherited * from parent) * * * `params` — optional object that specifies default values for * additional params that this state accepts; these params * will be encoded into query string * * * `component` — optional Vue component to be rendered * when entering this state * * * `redirect` — optional state name to redirect when navigating * to this state; typically used in "abstract" states (e.g. layouts, * data-preparation, etc.) to specify default sub-state * * * `enter` — optional `function(ctx, transition) => Promise` hook invoked * when transitioning into this state or its descendants * * * `leave` — optional `function(ctx, transition) => Promise` hook invoked * when transitioning from this state or its descendants * * * `handleError` — optional `function(err, ctx) => Promise` invoked * when error occurs during transition into/over this state * * @param manager * @param spec */ function State(manager, spec) { (0, _classCallCheck3.default)(this, State); this.manager = manager; this._setupName(spec); this._setupHierarchy(spec); this._setupComponent(spec); this._setupHooks(spec); this._setupPath(spec); this._setupParams(spec); this._setupOptions(spec); } (0, _createClass3.default)(State, [{ key: '_setupName', value: function _setupName(spec) { this.name = spec.name; if (!this.name) { throw new Error('State `name` is required'); } } }, { key: '_setupHierarchy', value: function _setupHierarchy(spec) { this.parentState = this.manager.get(spec.parent || this.name.split('.').slice(0, -1).join('.')) || null; this.lineage = this.parentState ? this.parentState.lineage.concat([this]) : [this]; } }, { key: '_setupComponent', value: function _setupComponent(spec) { if (spec.component) { this.component = spec.component; if (!this.component.name) { this.component.name = this.name.replace(/\./g, '-'); } } } }, { key: '_setupHooks', value: function _setupHooks(spec) { if (spec.enter) { this.enter = spec.enter; } if (spec.leave) { this.leave = spec.leave; } if (spec.handleError) { this.handleError = spec.handleError; } } }, { key: '_setupPath', value: function _setupPath(spec) { if (!spec.path && spec.url) { /* eslint-disable no-console */ console.warn('state.url is deprecated; use state.path instead'); /* eslint-enable no-console */ spec.path = spec.url; } this.path = spec.path || ''; if (this.path.indexOf('/') === 0) { this.fullPath = this.path; } else { var parentPath = this.parentState ? this.parentState.fullPath : '/'; this.fullPath = parentPath.replace(/\/+$/, '') + (this.path ? '/' + this.path : ''); } if (!this.fullPath) { this.fullPath = '/'; } this._pathParams = []; this._pathRegex = (0, _pathToRegexp2.default)(this.fullPath, this._pathParams); this._pathFormat = _pathToRegexp2.default.compile(this.fullPath); } }, { key: '_setupParams', value: function _setupParams(spec) { var _this = this; this._paramsSpec = {}; this._pathParams.forEach(function (param) { _this._paramsSpec[param.name] = null; }); (0, _assign2.default)(this._paramsSpec, spec.params); } }, { key: '_setupOptions', value: function _setupOptions(spec) { if (spec.redirect) { this.redirect = spec.redirect; } } /** * Invoked when going "downstream" (either into this state or * through this state to one of its descendants). * * Primary usage is to process `ctx.params` and populate `ctx.data`. * * Can redirect to another state by resolving `{ redirect: anotherStateName }` * (e.g. for authentication). * * It is also possible to render a custom Vue component instead * by resolving `{ component: customVueComponent }`. * * @param ctx — object `{ state, params, data }` * @returns {Promise} */ }, { key: 'enter', value: function enter(ctx) { return _promise2.default.resolve(); } /** * Invoked when going "upstream" (either from this state or * through this state from one of its descendants). * Typically used from cleaning up stuff used by this state. * * Like with `enter`, it is possible to redirect to another state * by resolving `{ redirect: anotherStateName }`. * * @param ctx — object `{ state, params, data }` * @returns {Promise} */ }, { key: 'leave', value: function leave(ctx) { return _promise2.default.resolve(); } /** * Invoked when error occurs during entering this state. * * Can redirect to another state by resolving `{ redirect: anotherStateName }` * (e.g. for authentication). * * It is also possible to render a custom Vue component instead * by resolving `{ component: customVueComponent }`. * * @param ctx — object `{ state, params, data }` * @returns {Promise} */ }, { key: 'handleError', value: function handleError(err, ctx) { return _promise2.default.reject(err); } /** * Returns true if this state is equal to `stateOrName` * or contains `stateOrName` somewhere up the hierarchy. * * @param {string|State} stateOrName * @return {boolean} */ }, { key: 'includes', value: function includes(stateOrName) { var state = stateOrName instanceof State ? stateOrName : this.manager.get(stateOrName); return this.lineage.indexOf(state) > -1; } /** * Attempts to match `location` to this state's `path` pattern. * * @param {{ pathname, search }} location * @returns an object with extracted params or `null` if don't match. * @private */ }, { key: '_match', value: function _match(location) { var matched = this._pathRegex.exec(location.pathname); if (!matched) { return null; } var params = this._pathParams.reduce(function (params, p, i) { params[p.name] = matched[i + 1]; return params; }, {}); try { var query = _queryString2.default.parse(location.search); (0, _assign2.default)(params, query); } catch (e) { // No query string or parsing failed — we don't care } return params; } /** * Constructs a `params` object by dropping any parameters * not specified in `_paramsSpec` of this state. * Values from `_paramsSpec` act as defaults. * * @param {object} params * @private */ }, { key: '_makeParams', value: function _makeParams(params) { var _this2 = this; return (0, _keys2.default)(this._paramsSpec).reduce(function (result, name) { result[name] = name in params ? params[name] : _this2._paramsSpec[name]; return result; }, {}); } /** * Constructs search string by serializing query params. * * @param params * @return {string} search * @private */ }, { key: '_makeSearch', value: function _makeSearch(params) { var query = (0, _keys2.default)(params).reduce(function (query, key) { var value = params[key]; if (value != null) { query[key] = value; } return query; }, {}); this._pathParams.forEach(function (p) { delete query[p.name]; }); try { var search = _queryString2.default.stringify(query); if (search) { return '?' + search; } } catch (e) { // No query string or serialization failed — we don't care } return ''; } /** * Constructs an URL by encoding `params` into URL pattern and query string. * * Note: params not mentioned in `_paramsSpec` are dropped. * * @param params * @return {string} url * @private */ }, { key: '_makeUrl', value: function _makeUrl(params) { return this._pathFormat(params) + this._makeSearch(params); } /** * Creates href suitable for links (taking into account base URL and * hash-based histories). * * @param params * @return {string} href */ }, { key: 'createHref', value: function createHref(params) { return this.manager.history.createHref(this._makeUrl(params)); } }]); return State; })(); exports.default = State; ; },{"babel-runtime/core-js/object/assign":1,"babel-runtime/core-js/object/keys":5,"babel-runtime/core-js/promise":7,"babel-runtime/helpers/classCallCheck":9,"babel-runtime/helpers/createClass":10,"path-to-regexp":118,"query-string":120}],127:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _keys = require('babel-runtime/core-js/object/keys'); var _keys2 = _interopRequireDefault(_keys); var _promise = require('babel-runtime/core-js/promise'); var _promise2 = _interopRequireDefault(_promise); var _typeof2 = require('babel-runtime/helpers/typeof'); var _typeof3 = _interopRequireDefault(_typeof2); var _assign = require('babel-runtime/core-js/object/assign'); var _assign2 = _interopRequireDefault(_assign); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _createClass2 = require('babel-runtime/helpers/createClass'); var _createClass3 = _interopRequireDefault(_createClass2); var _debug = require('debug'); var _debug2 = _interopRequireDefault(_debug); var _error = require('./error'); var _utils = require('./utils'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var debug = (0, _debug2.default)('voie:transition'); var Transition = (function () { function Transition(manager) { (0, _classCallCheck3.default)(this, Transition); this.manager = manager; this.redirectsCount = 0; this.params = (0, _assign2.default)({}, manager.context.params); } (0, _createClass3.default)(Transition, [{ key: 'go', value: function go(name, params, isRedirect) { var _this = this; debug(isRedirect ? 'redirect to %s' : 'go to %s', name); (0, _assign2.default)(this.params, params || {}); var state = this.manager.get(name); if (!state) { throw new _error.StateNotFoundError(name); } this.dstState = state; if (state.redirect) { return this.handleRedirect(state.redirect); } return this.goUpstream().then(function () { return _this.goDownstream(); }); } }, { key: 'handleRedirect', value: function handleRedirect(redirect) { var _this2 = this; this.redirectsCount++; if (this.redirectsCount > this.manager.maxRedirects) { throw new _error.RedirectLoopError(this); } switch (typeof redirect === 'undefined' ? 'undefined' : (0, _typeof3.default)(redirect)) { case 'string': return this.go(redirect, {}, true); case 'object': return this.go(redirect.name, redirect.params, true); case 'function': return _promise2.default.resolve().then(function () { return redirect(_this2); }).then(function (redirect) { return _this2.handleRedirect(redirect); }); default: throw new Error('Unknown redirect: ' + redirect); } } }, { key: 'goUpstream', value: function goUpstream() { var _this3 = this; var ctx = this.manager.context; if (!ctx.state) { // We're at root state, no cleanup is necessary return _promise2.default.resolve(); } // Stop going up if state is common with dst branch var state = ctx.state; if (this.dstState.includes(state)) { // All ctx params must match target ones // (e.g. when going from /user/1 to /user/2) var paramsMatch = (0, _keys2.default)(ctx.params).every(function (key) { return ctx.params[key] === _this3.params[key]; }); if (paramsMatch) { return _promise2.default.resolve(); } } return _promise2.default.resolve().then(function () { return state.leave(ctx); }).then(function () { return _this3.manager.afterEach(ctx); }).then(function () { debug(' <- left %s', state.name); _this3.cleanup(ctx); }).then(function () { return _this3.goUpstream(); }); } }, { key: 'cleanup', value: function cleanup(ctx) { if (ctx.vm) { // Destroy vm and restore v-view element var el = ctx.vm.$el; var mp = ctx.mountPoint; ctx.vm.$destroy(); if (mp) { (function () { var viewEl = ctx.mountPoint.viewEl; el.parentNode.replaceChild(viewEl, el); mp.viewElChildren.forEach(function (el) { return viewEl.appendChild(el); }); })(); } } this.manager.context = ctx.parent; this.manager.emit('context_updated', this.manager.context); } }, { key: 'goDownstream', value: function goDownstream() { var _this4 = this; var prevCtx = this.manager.context; var dstLineage = this.dstState.lineage; var nextState = dstLineage[dstLineage.indexOf(prevCtx.state) + 1]; if (!nextState) { return _promise2.default.resolve(); } // New context inherits params and data from parent var nextContext = { parent: prevCtx, state: nextState, params: (0, _assign2.default)({}, prevCtx.params, nextState._makeParams(this.params)), data: (0, _assign2.default)({}, prevCtx.data) }; return _promise2.default.resolve(true).then(function () { return _this4.manager.beforeEach(nextContext); }).catch(function (err) { return nextState.handleError(err, nextContext); }).then(function (obj) { return _this4._handleEnterHook(obj, nextContext); }).then(function (proceed) { if (!proceed) { return false; } return _promise2.default.resolve().then(function () { return nextState.enter(nextContext); }).catch(function (err) { return nextState.handleError(err, nextContext); }).then(function (obj) { return _this4._handleEnterHook(obj, nextContext); }); }).then(function (proceed) { if (!proceed) { return false; } _this4.manager.context = nextContext; _this4.manager.emit('context_updated', _this4.manager.context); _this4.render(nextContext, nextState.component); if (nextState !== _this4.dstState) { return _this4.goDownstream(); } }); } /** * @return {Boolean} proceed * @private */ }, { key: '_handleEnterHook', value: function _handleEnterHook(obj, nextContext) { obj = obj || {}; var nextState = nextContext.state; debug(' -> entered %s', nextState.name); // hooks can return { redirect: 'new.state.name' } // or { redirect: { name, params } } if (obj.redirect) { return this.handleRedirect(obj.redirect).then(function () { return false; }); } // hooks can also return { component: } var rendered = this.render(nextContext, obj.component); return !rendered; } }, { key: 'render', value: function render(ctx, comp) { if (!comp) { return false; } var Comp = (0, _utils.toVueComponent)(comp); var mp = this.manager._getMountPoint(); ctx.mountPoint = mp; ctx.vm = new Comp({ data: ctx.data, el: mp.viewEl, parent: mp.hostVm, params: ctx.params, ctx: ctx, state: ctx.state, manager: this.manager }); return true; } }, { key: 'to', get: function get() { return this.dstState; } }]); return Transition; })(); exports.default = Transition; },{"./error":123,"./utils":128,"babel-runtime/core-js/object/assign":1,"babel-runtime/core-js/object/keys":5,"babel-runtime/core-js/promise":7,"babel-runtime/helpers/classCallCheck":9,"babel-runtime/helpers/createClass":10,"babel-runtime/helpers/typeof":13,"debug":88}],128:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.toVueComponent = toVueComponent; var _vue = require('vue'); var _vue2 = _interopRequireDefault(_vue); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function toVueComponent(obj) { if (obj.name === 'VueComponent') { return obj; } return _vue2.default.extend(obj); } },{"vue":"vue"}]},{},[124])(124) }); ================================================ FILE: lib/directives.js ================================================ 'use strict'; var _keys = require('babel-runtime/core-js/object/keys'); var _keys2 = _interopRequireDefault(_keys); var _assign = require('babel-runtime/core-js/object/assign'); var _assign2 = _interopRequireDefault(_assign); var _vue = require('vue'); var _vue2 = _interopRequireDefault(_vue); var _debug = require('debug'); var _debug2 = _interopRequireDefault(_debug); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var debug = (0, _debug2.default)('voie:directive'); _vue2.default.elementDirective('v-view', { bind: function bind() { var _vm$$options = this.vm.$options; var state = _vm$$options.state; var manager = _vm$$options.manager; manager.mountPoints[state.name] = { hostVm: this.vm, viewEl: this.el, viewElChildren: [].slice.call(this.el.children) }; debug('registered v-view', this.el); }, unbind: function unbind() { var _vm$$options2 = this.vm.$options; var state = _vm$$options2.state; var manager = _vm$$options2.manager; delete manager.mountPoints[state.name]; debug('unregistered v-view', this.el); } }); _vue2.default.directive('link', { bind: function bind() { this.manager = resolveManager(this.vm); if (!this.manager) { throw new Error('Can\'t locate state manager.'); } this.manager.on('context_updated', this.updateElement, this); }, unbind: function unbind() { this.manager.off('context_updated', this.updateElement, this); }, update: function update(value) { var _this = this; if (!value) { throw new Error('v-link: expression "' + this.expression + '" should resolve to { name: ..., params... }}'); } var manager = this.manager; var name = null; if (typeof value == 'string') { name = value; this.params = {}; } else { name = value.name; this.params = value.params || {}; } this.state = manager.get(name); if (!this.state) { /* eslint-disable no-console */ console.warn('State "' + name + '" not found.'); /* eslint-enable no-console */ return; } this.el.onclick = function (ev) { ev.preventDefault(); manager.go({ name: name, params: _this.params }); }; this.updateElement(); }, updateElement: function updateElement() { var manager = this.manager; var ctx = manager.context; var state = this.state; if (!state) { return; } var params = (0, _assign2.default)({}, ctx.params, this.params); this.el.setAttribute('href', state.createHref(params)); // Add/remove active class this.el.classList.remove(manager.activeClass); if (ctx.state) { var paramsMatch = (0, _keys2.default)(params).every(function (key) { return ctx.params[key] === params[key]; }); var active = ctx.state.includes(state) && paramsMatch; if (active) { this.el.classList.add(manager.activeClass); } else { this.el.classList.remove(manager.activeClass); } } } }); function resolveManager(vm) { var manager = vm.$options.manager; if (manager) { return manager; } if (vm.$parent) { return resolveManager(vm.$parent); } return null; } ================================================ FILE: lib/error.js ================================================ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.RedirectLoopError = exports.StateNotFoundError = undefined; var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); var _inherits2 = require('babel-runtime/helpers/inherits'); var _inherits3 = _interopRequireDefault(_inherits2); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var StateNotFoundError = exports.StateNotFoundError = (function (_Error) { (0, _inherits3.default)(StateNotFoundError, _Error); function StateNotFoundError(name) { (0, _classCallCheck3.default)(this, StateNotFoundError); var _this = (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(StateNotFoundError).call(this, 'State "' + name + '" not found.')); _this.name = name; return _this; } return StateNotFoundError; })(Error); var RedirectLoopError = exports.RedirectLoopError = (function (_Error2) { (0, _inherits3.default)(RedirectLoopError, _Error2); function RedirectLoopError(transition) { (0, _classCallCheck3.default)(this, RedirectLoopError); var _this2 = (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(RedirectLoopError).call(this, 'Redirect loop detected ' + '(set maxRedirects on state manager to configure max redirects per transition)')); _this2.transition = transition; return _this2; } return RedirectLoopError; })(Error); ================================================ FILE: lib/index.js ================================================ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.Transition = exports.State = exports.StateManager = undefined; var _stateManager = require('./state-manager'); var _stateManager2 = _interopRequireDefault(_stateManager); var _state = require('./state'); var _state2 = _interopRequireDefault(_state); var _transition = require('./transition'); var _transition2 = _interopRequireDefault(_transition); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } exports.StateManager = _stateManager2.default; exports.State = _state2.default; exports.Transition = _transition2.default; ================================================ FILE: lib/state-manager.js ================================================ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _keys = require('babel-runtime/core-js/object/keys'); var _keys2 = _interopRequireDefault(_keys); var _assign = require('babel-runtime/core-js/object/assign'); var _assign2 = _interopRequireDefault(_assign); var _typeof2 = require('babel-runtime/helpers/typeof'); var _typeof3 = _interopRequireDefault(_typeof2); var _promise = require('babel-runtime/core-js/promise'); var _promise2 = _interopRequireDefault(_promise); var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _createClass2 = require('babel-runtime/helpers/createClass'); var _createClass3 = _interopRequireDefault(_createClass2); var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); var _inherits2 = require('babel-runtime/helpers/inherits'); var _inherits3 = _interopRequireDefault(_inherits2); var _debug = require('debug'); var _debug2 = _interopRequireDefault(_debug); var _eventemitter = require('eventemitter3'); var _eventemitter2 = _interopRequireDefault(_eventemitter); var _state = require('./state'); var _state2 = _interopRequireDefault(_state); var _transition = require('./transition'); var _transition2 = _interopRequireDefault(_transition); var _history = require('history'); require('./directives'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var debug = (0, _debug2.default)('voie:manager'); /** * State manager holds the hierarchy of application states, * exposes methods for navigating around states and * keeps track of current state and its `context` * (object `{ state, params, data, ... }`). * * A typical application will have a single instance * of state manager exposed as a module. * * ```es6 * import { StateManager } from 'voie'; * * export default new StateManager({ ... }); * ``` * * State manager emits following events: * * * `history_updated` * * `context_updated` * * `transition_finished` * */ var StateManager = (function (_EventEmitter) { (0, _inherits3.default)(StateManager, _EventEmitter); /** * Instantiates new state manager. * * Options: * * * `el` — required, root DOM element for rendering views * (can be either `HTMLElement` or selector string) * * * history — a history object (see `rackt/history`) * * * base — (only used when `history` is not specified), base href * for application (URL pathname prefix) * * * `maxRedirects` — maximum number of redirects within * a single transition, when exceeded transition will fail with * `RedirectLoopError` (default is 10) * * * `activeClass` — active class for `v-link` directive * (default is "active")' * * * `handleUncaught` — `function(err) => Promise` invoked * when transition fails with error * * * `beforeEach` — `function(ctx) => Promise` invoked * before each `enter` hook */ function StateManager(spec) { (0, _classCallCheck3.default)(this, StateManager); var _this = (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(StateManager).call(this)); _this._setupEl(spec); _this._setupHistory(spec); _this._setupOptions(spec); _this._setupHooks(spec); _this._setupState(); return _this; } (0, _createClass3.default)(StateManager, [{ key: '_setupEl', value: function _setupEl(spec) { this.el = spec.el instanceof HTMLElement ? spec.el : document.querySelector(spec.el); if (!this.el) { throw new Error('Please specify `el` as an entry-point node of your app.'); } } }, { key: '_setupHistory', value: function _setupHistory(spec) { if (spec.history) { this.history = spec.history; } else { this._setupDefaultHtml5History(spec); } } }, { key: '_setupDefaultHtml5History', value: function _setupDefaultHtml5History(spec) { var base = spec.base; // Try to take base from `` if (!base) { var baseEl = document.querySelector('base'); base = baseEl && baseEl.getAttribute('href'); } base = (base || '').replace(/\/+$/, ''); this.history = (0, _history.useBasename)(_history.createHistory)({ basename: base }); } }, { key: '_setupOptions', value: function _setupOptions(spec) { if (spec.handleUncaught) { this.handleUncaught = spec.handleUncaught; } this.maxRedirects = Number(spec.maxRedirects) || 10; this.activeClass = spec.activeClass || 'active'; } }, { key: '_setupHooks', value: function _setupHooks(spec) { if (spec.beforeEach) { this.beforeEach = spec.beforeEach; } if (spec.afterEach) { this.afterEach = spec.afterEach; } } }, { key: '_setupState', value: function _setupState() { this.states = {}; this.context = { parent: null, state: null, // root state vm: null, params: {}, data: {} }; this.mountPoints = { '': { viewEl: this.el, viewElChildren: [].slice.call(this.el.children) } }; this.transition = null; } /** * Executed before `enter` hooks on each state. * * @returns {Promise} */ }, { key: 'beforeEach', value: function beforeEach() {} /** * Executed after `leave` hooks on each state. * * @returns {Promise} */ }, { key: 'afterEach', value: function afterEach() {} /** * Handles errors uncaught during transition. * Used for overriding on a per-instance basis. * * @returns {Promise} */ }, { key: 'handleUncaught', value: function handleUncaught(err) { return _promise2.default.reject(err); } /** * Registers a new state with specified `name`. * * You can use one of two styles: * * ```es6 * sm.add('foo', { ... }); * sm.add({ name: 'foo', ... }); * ``` * * All options are passed to `State` constructor. * * @returns {State} */ }, { key: 'add', value: function add(name, spec) { if ((typeof name === 'undefined' ? 'undefined' : (0, _typeof3.default)(name)) == 'object') { spec = name; name = spec.name; } if (!name) { throw new Error('State `name` is mandatory.'); } if (this.states[name]) { throw new Error('State "' + name + '" already added'); } spec.name = name; var state = new _state2.default(this, spec); debug('add %s', name); this.states[name] = state; return state; } /** * Retrieves a state previously registered via `add`. * * @returns {State} */ }, { key: 'get', value: function get(name) { return this.states[name]; } /** * Navigates to a state with specified `name`. * Navigation is performed asynchronously, allowing * state enter/leave hook to perform async tasks * (e.g. fetch data). * * Throws an exception if another transition is * taking place. * * Options: * * * `name` — target state name (if null, will transition to same state * with updated params) * * `params` — object containing state parameters * (either path variables or query string parameters) * * `replace` — if `true` don't create a separate record * in browser history (default is `false`) * * Transition process: * * * find nearest common ancestor state with matching parameters * * go "upstream", leaving states, cleaning up states, * destroying components * * go "downstream", entering states, instantiating and rendering * components * * update browser history to reflect target state * (e.g. set new URL in address bar) * * Note that it is the responsibility of the state to compute * target URL. * * @returns {Promise} resolved when navigation is finished. */ }, { key: 'go', value: function go(options) { var _this2 = this; if (this.transition) { throw new Error('Transition is in progress.'); } this.transition = new _transition2.default(this); var currentState = this.context.state; var name = null; if (typeof options === 'string') { name = options; options = {}; } else if (options.name) { name = options.name; } else if (currentState) { name = currentState.name; } else { throw new Error('Destination state not specified'); } return _promise2.default.resolve().then(function () { return _this2.transition.go(name, options.params || {}); }).then(function (result) { _this2.transition = null; _this2.emit('transition_finished'); return result; }).catch(function (err) { _this2.transition = null; _this2.emit('transition_finished', err); return _this2.handleUncaught(err); }).then(function () { return _this2._updateHistory(options.replace || false); }); } /** * Updates browser history without actually performing * any transitions. * * Typically used to serialize parameters into query string * without performing navigation (e.g. when params are used * only in Vue components). */ }, { key: 'update', value: function update(params, replace) { var _this3 = this; (0, _assign2.default)(this.context.params, params); this.emit('context_updated', this.context); return _promise2.default.resolve().then(function () { return _this3._updateHistory(replace); }); } /** * Resolves nearest mount point in current context tree. * * Mount point is a "slot" that corresponds to `v-view` directive * (and the component that hosts it). */ }, { key: '_getMountPoint', value: function _getMountPoint() { var ctx = this.context; var el = null; while (ctx && !el) { var state = ctx.state; if (state) { el = this.mountPoints[state.name]; } else { el = this.mountPoints['']; } ctx = ctx.parent; } return el; } /** * Begins listening for history events (e.g. browser back button) * and performs initial navigation by matching current URL. * * @returns {Promise} resolved when initial navigation is finished. */ }, { key: 'start', value: function start() { var _this4 = this; if (this._unlisten) { return _promise2.default.resolve(); } this._unlisten = this.history.listen(function (location) { return _this4._matchLocation(location); }); return new _promise2.default(function (resolve) { return _this4.once('history_updated', resolve); }); } /** * Stops listening for history events. */ }, { key: 'stop', value: function stop() { if (!this._unlisten) { return; } this._unlisten(); delete this._unlisten; } }, { key: '_matchLocation', value: function _matchLocation(location) { var _this5 = this; var url = location.pathname + location.search; if (url === this.context.url) { return; } var found = (0, _keys2.default)(this.states).find(function (name) { var state = _this5.states[name]; var matched = state._match(location); if (matched) { debug('match url %s -> %s', location.pathname, name); _this5.go({ name: name, params: matched, replace: true }); return true; } }); if (!found) { /* eslint-disable no-console */ console.warn('No states match URL: ' + location.pathname); /* eslint-enable no-console */ this._updateHistory(true); } } }, { key: '_updateHistory', value: function _updateHistory(replace) { var state = this.context.state; var url = state ? state._makeUrl(this.context.params) : '/'; if (url === this.context.url) { return; } this.context.url = url; if (replace) { this.history.replace(url); } else { this.history.push(url); } this.emit('history_updated', { url: url, ctx: this.context }); } }]); return StateManager; })(_eventemitter2.default); exports.default = StateManager; ; ================================================ FILE: lib/state.js ================================================ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _keys = require('babel-runtime/core-js/object/keys'); var _keys2 = _interopRequireDefault(_keys); var _promise = require('babel-runtime/core-js/promise'); var _promise2 = _interopRequireDefault(_promise); var _assign = require('babel-runtime/core-js/object/assign'); var _assign2 = _interopRequireDefault(_assign); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _createClass2 = require('babel-runtime/helpers/createClass'); var _createClass3 = _interopRequireDefault(_createClass2); var _pathToRegexp = require('path-to-regexp'); var _pathToRegexp2 = _interopRequireDefault(_pathToRegexp); var _queryString = require('query-string'); var _queryString2 = _interopRequireDefault(_queryString); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * Represents a node in application states hierarchy. * * Most state features are optional, the only required * setting is a unique `name` and a reference to `parent` state. * * Depending on features a state can represent: * * * an abstract view (e.g. layout containing a component with `` * directive) * * a concrete view * * a route with path pattern and query parameters * * a data resolver that provides data to its descendants * * a mix of above * * Invoking constructor directly is not recommended, * use `StateManager#add` instead. * * @private */ var State = (function () { /** * Options: * * * `name` — required, state name; if contains dots `.` * then parent state will be inferred from this name * (e.g. `layout.users` will become a parent of `layout.users.list`) * unless `parent` is specified explicitly * * * `parent` — optional, parent state (this option disables * parent-from-name inference) * * * `path` — optional pathname pattern for matching URLs and * extracting parameters (e.g. `/user/:userId`). If started with `/`, * then path is treated as an absolute pathname, otherwise * path is treated as a fragment (absolute pathname is inherited * from parent) * * * `params` — optional object that specifies default values for * additional params that this state accepts; these params * will be encoded into query string * * * `component` — optional Vue component to be rendered * when entering this state * * * `redirect` — optional state name to redirect when navigating * to this state; typically used in "abstract" states (e.g. layouts, * data-preparation, etc.) to specify default sub-state * * * `enter` — optional `function(ctx, transition) => Promise` hook invoked * when transitioning into this state or its descendants * * * `leave` — optional `function(ctx, transition) => Promise` hook invoked * when transitioning from this state or its descendants * * * `handleError` — optional `function(err, ctx) => Promise` invoked * when error occurs during transition into/over this state * * @param manager * @param spec */ function State(manager, spec) { (0, _classCallCheck3.default)(this, State); this.manager = manager; this._setupName(spec); this._setupHierarchy(spec); this._setupComponent(spec); this._setupHooks(spec); this._setupPath(spec); this._setupParams(spec); this._setupOptions(spec); } (0, _createClass3.default)(State, [{ key: '_setupName', value: function _setupName(spec) { this.name = spec.name; if (!this.name) { throw new Error('State `name` is required'); } } }, { key: '_setupHierarchy', value: function _setupHierarchy(spec) { this.parentState = this.manager.get(spec.parent || this.name.split('.').slice(0, -1).join('.')) || null; this.lineage = this.parentState ? this.parentState.lineage.concat([this]) : [this]; } }, { key: '_setupComponent', value: function _setupComponent(spec) { if (spec.component) { this.component = spec.component; if (!this.component.name) { this.component.name = this.name.replace(/\./g, '-'); } } } }, { key: '_setupHooks', value: function _setupHooks(spec) { if (spec.enter) { this.enter = spec.enter; } if (spec.leave) { this.leave = spec.leave; } if (spec.handleError) { this.handleError = spec.handleError; } } }, { key: '_setupPath', value: function _setupPath(spec) { if (!spec.path && spec.url) { /* eslint-disable no-console */ console.warn('state.url is deprecated; use state.path instead'); /* eslint-enable no-console */ spec.path = spec.url; } this.path = spec.path || ''; if (this.path.indexOf('/') === 0) { this.fullPath = this.path; } else { var parentPath = this.parentState ? this.parentState.fullPath : '/'; this.fullPath = parentPath.replace(/\/+$/, '') + (this.path ? '/' + this.path : ''); } if (!this.fullPath) { this.fullPath = '/'; } this._pathParams = []; this._pathRegex = (0, _pathToRegexp2.default)(this.fullPath, this._pathParams); this._pathFormat = _pathToRegexp2.default.compile(this.fullPath); } }, { key: '_setupParams', value: function _setupParams(spec) { var _this = this; this._paramsSpec = {}; this._pathParams.forEach(function (param) { _this._paramsSpec[param.name] = null; }); (0, _assign2.default)(this._paramsSpec, spec.params); } }, { key: '_setupOptions', value: function _setupOptions(spec) { if (spec.redirect) { this.redirect = spec.redirect; } } /** * Invoked when going "downstream" (either into this state or * through this state to one of its descendants). * * Primary usage is to process `ctx.params` and populate `ctx.data`. * * Can redirect to another state by resolving `{ redirect: anotherStateName }` * (e.g. for authentication). * * It is also possible to render a custom Vue component instead * by resolving `{ component: customVueComponent }`. * * @param ctx — object `{ state, params, data }` * @returns {Promise} */ }, { key: 'enter', value: function enter(ctx) { return _promise2.default.resolve(); } /** * Invoked when going "upstream" (either from this state or * through this state from one of its descendants). * Typically used from cleaning up stuff used by this state. * * Like with `enter`, it is possible to redirect to another state * by resolving `{ redirect: anotherStateName }`. * * @param ctx — object `{ state, params, data }` * @returns {Promise} */ }, { key: 'leave', value: function leave(ctx) { return _promise2.default.resolve(); } /** * Invoked when error occurs during entering this state. * * Can redirect to another state by resolving `{ redirect: anotherStateName }` * (e.g. for authentication). * * It is also possible to render a custom Vue component instead * by resolving `{ component: customVueComponent }`. * * @param ctx — object `{ state, params, data }` * @returns {Promise} */ }, { key: 'handleError', value: function handleError(err, ctx) { return _promise2.default.reject(err); } /** * Returns true if this state is equal to `stateOrName` * or contains `stateOrName` somewhere up the hierarchy. * * @param {string|State} stateOrName * @return {boolean} */ }, { key: 'includes', value: function includes(stateOrName) { var state = stateOrName instanceof State ? stateOrName : this.manager.get(stateOrName); return this.lineage.indexOf(state) > -1; } /** * Attempts to match `location` to this state's `path` pattern. * * @param {{ pathname, search }} location * @returns an object with extracted params or `null` if don't match. * @private */ }, { key: '_match', value: function _match(location) { var matched = this._pathRegex.exec(location.pathname); if (!matched) { return null; } var params = this._pathParams.reduce(function (params, p, i) { params[p.name] = matched[i + 1]; return params; }, {}); try { var query = _queryString2.default.parse(location.search); (0, _assign2.default)(params, query); } catch (e) { // No query string or parsing failed — we don't care } return params; } /** * Constructs a `params` object by dropping any parameters * not specified in `_paramsSpec` of this state. * Values from `_paramsSpec` act as defaults. * * @param {object} params * @private */ }, { key: '_makeParams', value: function _makeParams(params) { var _this2 = this; return (0, _keys2.default)(this._paramsSpec).reduce(function (result, name) { result[name] = name in params ? params[name] : _this2._paramsSpec[name]; return result; }, {}); } /** * Constructs search string by serializing query params. * * @param params * @return {string} search * @private */ }, { key: '_makeSearch', value: function _makeSearch(params) { var query = (0, _keys2.default)(params).reduce(function (query, key) { var value = params[key]; if (value != null) { query[key] = value; } return query; }, {}); this._pathParams.forEach(function (p) { delete query[p.name]; }); try { var search = _queryString2.default.stringify(query); if (search) { return '?' + search; } } catch (e) { // No query string or serialization failed — we don't care } return ''; } /** * Constructs an URL by encoding `params` into URL pattern and query string. * * Note: params not mentioned in `_paramsSpec` are dropped. * * @param params * @return {string} url * @private */ }, { key: '_makeUrl', value: function _makeUrl(params) { return this._pathFormat(params) + this._makeSearch(params); } /** * Creates href suitable for links (taking into account base URL and * hash-based histories). * * @param params * @return {string} href */ }, { key: 'createHref', value: function createHref(params) { return this.manager.history.createHref(this._makeUrl(params)); } }]); return State; })(); exports.default = State; ; ================================================ FILE: lib/transition.js ================================================ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _keys = require('babel-runtime/core-js/object/keys'); var _keys2 = _interopRequireDefault(_keys); var _promise = require('babel-runtime/core-js/promise'); var _promise2 = _interopRequireDefault(_promise); var _typeof2 = require('babel-runtime/helpers/typeof'); var _typeof3 = _interopRequireDefault(_typeof2); var _assign = require('babel-runtime/core-js/object/assign'); var _assign2 = _interopRequireDefault(_assign); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _createClass2 = require('babel-runtime/helpers/createClass'); var _createClass3 = _interopRequireDefault(_createClass2); var _debug = require('debug'); var _debug2 = _interopRequireDefault(_debug); var _error = require('./error'); var _utils = require('./utils'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var debug = (0, _debug2.default)('voie:transition'); var Transition = (function () { function Transition(manager) { (0, _classCallCheck3.default)(this, Transition); this.manager = manager; this.redirectsCount = 0; this.params = (0, _assign2.default)({}, manager.context.params); } (0, _createClass3.default)(Transition, [{ key: 'go', value: function go(name, params, isRedirect) { var _this = this; debug(isRedirect ? 'redirect to %s' : 'go to %s', name); (0, _assign2.default)(this.params, params || {}); var state = this.manager.get(name); if (!state) { throw new _error.StateNotFoundError(name); } this.dstState = state; if (state.redirect) { return this.handleRedirect(state.redirect); } return this.goUpstream().then(function () { return _this.goDownstream(); }); } }, { key: 'handleRedirect', value: function handleRedirect(redirect) { var _this2 = this; this.redirectsCount++; if (this.redirectsCount > this.manager.maxRedirects) { throw new _error.RedirectLoopError(this); } switch (typeof redirect === 'undefined' ? 'undefined' : (0, _typeof3.default)(redirect)) { case 'string': return this.go(redirect, {}, true); case 'object': return this.go(redirect.name, redirect.params, true); case 'function': return _promise2.default.resolve().then(function () { return redirect(_this2); }).then(function (redirect) { return _this2.handleRedirect(redirect); }); default: throw new Error('Unknown redirect: ' + redirect); } } }, { key: 'goUpstream', value: function goUpstream() { var _this3 = this; var ctx = this.manager.context; if (!ctx.state) { // We're at root state, no cleanup is necessary return _promise2.default.resolve(); } // Stop going up if state is common with dst branch var state = ctx.state; if (this.dstState.includes(state)) { // All ctx params must match target ones // (e.g. when going from /user/1 to /user/2) var paramsMatch = (0, _keys2.default)(ctx.params).every(function (key) { return ctx.params[key] === _this3.params[key]; }); if (paramsMatch) { return _promise2.default.resolve(); } } return _promise2.default.resolve().then(function () { return state.leave(ctx); }).then(function () { return _this3.manager.afterEach(ctx); }).then(function () { debug(' <- left %s', state.name); _this3.cleanup(ctx); }).then(function () { return _this3.goUpstream(); }); } }, { key: 'cleanup', value: function cleanup(ctx) { if (ctx.vm) { // Destroy vm and restore v-view element var el = ctx.vm.$el; var mp = ctx.mountPoint; ctx.vm.$destroy(); if (mp) { (function () { var viewEl = ctx.mountPoint.viewEl; el.parentNode.replaceChild(viewEl, el); mp.viewElChildren.forEach(function (el) { return viewEl.appendChild(el); }); })(); } } this.manager.context = ctx.parent; this.manager.emit('context_updated', this.manager.context); } }, { key: 'goDownstream', value: function goDownstream() { var _this4 = this; var prevCtx = this.manager.context; var dstLineage = this.dstState.lineage; var nextState = dstLineage[dstLineage.indexOf(prevCtx.state) + 1]; if (!nextState) { return _promise2.default.resolve(); } // New context inherits params and data from parent var nextContext = { parent: prevCtx, state: nextState, params: (0, _assign2.default)({}, prevCtx.params, nextState._makeParams(this.params)), data: (0, _assign2.default)({}, prevCtx.data) }; return _promise2.default.resolve(true).then(function () { return _this4.manager.beforeEach(nextContext); }).catch(function (err) { return nextState.handleError(err, nextContext); }).then(function (obj) { return _this4._handleEnterHook(obj, nextContext); }).then(function (proceed) { if (!proceed) { return false; } return _promise2.default.resolve().then(function () { return nextState.enter(nextContext); }).catch(function (err) { return nextState.handleError(err, nextContext); }).then(function (obj) { return _this4._handleEnterHook(obj, nextContext); }); }).then(function (proceed) { if (!proceed) { return false; } _this4.manager.context = nextContext; _this4.manager.emit('context_updated', _this4.manager.context); _this4.render(nextContext, nextState.component); if (nextState !== _this4.dstState) { return _this4.goDownstream(); } }); } /** * @return {Boolean} proceed * @private */ }, { key: '_handleEnterHook', value: function _handleEnterHook(obj, nextContext) { obj = obj || {}; var nextState = nextContext.state; debug(' -> entered %s', nextState.name); // hooks can return { redirect: 'new.state.name' } // or { redirect: { name, params } } if (obj.redirect) { return this.handleRedirect(obj.redirect).then(function () { return false; }); } // hooks can also return { component: } var rendered = this.render(nextContext, obj.component); return !rendered; } }, { key: 'render', value: function render(ctx, comp) { if (!comp) { return false; } var Comp = (0, _utils.toVueComponent)(comp); var mp = this.manager._getMountPoint(); ctx.mountPoint = mp; ctx.vm = new Comp({ data: ctx.data, el: mp.viewEl, parent: mp.hostVm, params: ctx.params, ctx: ctx, state: ctx.state, manager: this.manager }); return true; } }, { key: 'to', get: function get() { return this.dstState; } }]); return Transition; })(); exports.default = Transition; ================================================ FILE: lib/utils.js ================================================ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.toVueComponent = toVueComponent; var _vue = require('vue'); var _vue2 = _interopRequireDefault(_vue); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function toVueComponent(obj) { if (obj.name === 'VueComponent') { return obj; } return _vue2.default.extend(obj); } ================================================ FILE: package.json ================================================ { "name": "voie", "version": "0.7.0", "description": "Voie.js — simple router / layout manager for Vue", "repository": { "type": "git", "url": "https://github.com/inca/voie" }, "main": "lib/index.js", "dependencies": { "debug": "^2.2.0", "eslint": "^2.8.0", "eventemitter3": "^1.1.1", "history": "^3.2.0", "path-to-regexp": "^1.2.1", "query-string": "^3.0.0", "vue": "^1.0.10" }, "devDependencies": { "babel-cli": "^6.3.15", "babel-plugin-transform-runtime": "^6.3.13", "babel-preset-es2015": "^6.3.13", "babel-runtime": "^5.8.34", "babelify": "^7.2.0", "browserify": "^13.0.0", "chai": "^3.4.1", "karma": "^0.13.15", "karma-browserify": "^4.4.2", "karma-chai": "^0.1.0", "karma-chrome-launcher": "^0.2.2", "karma-mocha": "^0.2.1", "karma-spec-reporter": "0.0.23", "mocha": "^2.3.4", "pre-commit": "^1.1.2", "uglify-js": "^2.6.1", "vue-hot-reload-api": "^1.2.2", "vueify": "^7.1.0", "vueify-insert-css": "^1.0.0" }, "scripts": { "check": "eslint .", "dev": "karma start ./test/karma.dev.conf.js", "build": "npm run build:compile && npm run build:standalone && npm run build:minify", "build:compile": "babel src --out-dir lib", "build:standalone": "browserify -s voie -x vue -e src/index.js -o dist/voie.js", "build:minify": "uglifyjs -o dist/voie.min.js -- dist/voie.js", "preversion": "npm run build" }, "pre-commit": [ "check" ], "browserify": { "transform": [ "babelify" ] }, "browser": "src/index.js", "keywords": [ "vue", "router", "layout", "manager", "ui", "browser" ], "author": "Boris Okunskiy", "license": "ISC", "bugs": { "url": "https://github.com/inca/voie/issues" }, "homepage": "https://github.com/inca/voie" } ================================================ FILE: src/directives.js ================================================ import Vue from 'vue'; import Debug from 'debug'; const debug = Debug('voie:directive'); Vue.elementDirective('v-view', { bind() { const { state, manager } = this.vm.$options; manager.mountPoints[state.name] = { hostVm: this.vm, viewEl: this.el, viewElChildren: [].slice.call(this.el.children) }; debug('registered v-view', this.el); }, unbind() { const { state, manager } = this.vm.$options; delete manager.mountPoints[state.name]; debug('unregistered v-view', this.el); } }); Vue.directive('link', { bind() { this.manager = resolveManager(this.vm); if (!this.manager) { throw new Error('Can\'t locate state manager.') } this.manager.on('context_updated', this.updateElement, this); }, unbind() { this.manager.off('context_updated', this.updateElement, this); }, update(value) { if (!value) { throw new Error('v-link: expression "' + this.expression + '" should resolve to { name: ..., params... }}') } const manager = this.manager; let name = null; if (typeof value == 'string') { name = value; this.params = {}; } else { name = value.name; this.params = value.params || {}; } this.state = manager.get(name); if (!this.state) { /* eslint-disable no-console */ console.warn('State "' + name + '" not found.'); /* eslint-enable no-console */ return; } this.el.onclick = (ev) => { ev.preventDefault(); manager.go({ name: name, params: this.params }); }; this.updateElement(); }, updateElement() { const manager = this.manager; const ctx = manager.context; const state = this.state; if (!state) { return; } const params = Object.assign({}, ctx.params, this.params); this.el.setAttribute('href', state.createHref(params)); // Add/remove active class this.el.classList.remove(manager.activeClass); if (ctx.state) { const paramsMatch = Object.keys(params) .every(key => ctx.params[key] === params[key]); const active = ctx.state.includes(state) && paramsMatch; if (active) { this.el.classList.add(manager.activeClass); } else { this.el.classList.remove(manager.activeClass); } } } }); function resolveManager(vm) { const manager = vm.$options.manager; if (manager) { return manager; } if (vm.$parent) { return resolveManager(vm.$parent); } return null; } ================================================ FILE: src/error.js ================================================ 'use strict'; export class StateNotFoundError extends Error { constructor(name) { super('State "' + name + '" not found.'); this.name = name; } } export class RedirectLoopError extends Error { constructor(transition) { super('Redirect loop detected ' + '(set maxRedirects on state manager to configure max redirects per transition)'); this.transition = transition; } } ================================================ FILE: src/index.js ================================================ import StateManager from './state-manager'; import State from './state'; import Transition from './transition'; export { StateManager, State, Transition }; ================================================ FILE: src/state-manager.js ================================================ import Debug from 'debug'; const debug = Debug('voie:manager'); import EventEmitter from 'eventemitter3'; import State from './state'; import Transition from './transition'; import createHistory from 'history/createBrowserHistory'; import './directives'; /** * State manager holds the hierarchy of application states, * exposes methods for navigating around states and * keeps track of current state and its `context` * (object `{ state, params, data, ... }`). * * A typical application will have a single instance * of state manager exposed as a module. * * ```es6 * import { StateManager } from 'voie'; * * export default new StateManager({ ... }); * ``` * * State manager emits following events: * * * `history_updated` * * `context_updated` * * `transition_finished` * */ export default class StateManager extends EventEmitter { /** * Instantiates new state manager. * * Options: * * * `el` — required, root DOM element for rendering views * (can be either `HTMLElement` or selector string) * * * history — a history object (see `rackt/history`) * * * base — (only used when `history` is not specified), base href * for application (URL pathname prefix) * * * `maxRedirects` — maximum number of redirects within * a single transition, when exceeded transition will fail with * `RedirectLoopError` (default is 10) * * * `activeClass` — active class for `v-link` directive * (default is "active")' * * * `handleUncaught` — `function(err) => Promise` invoked * when transition fails with error * * * `beforeEach` — `function(ctx) => Promise` invoked * before each `enter` hook */ constructor(spec) { super(); this._setupEl(spec); this._setupHistory(spec); this._setupOptions(spec); this._setupHooks(spec); this._setupState(); } _setupEl(spec) { this.el = spec.el instanceof HTMLElement ? spec.el : document.querySelector(spec.el); if (!this.el) { throw new Error('Please specify `el` as an entry-point node of your app.') } } _setupHistory(spec) { if (spec.history) { this.history = spec.history; } else { this._setupDefaultHtml5History(spec); } } _setupDefaultHtml5History(spec) { let base = spec.base; // Try to take base from `` if (!base) { const baseEl = document.querySelector('base'); base = baseEl && baseEl.getAttribute('href'); } base = (base || '').replace(/\/+$/, ''); this.history = createHistory({ basename: base }); } _setupOptions(spec) { if (spec.handleUncaught) { this.handleUncaught = spec.handleUncaught; } this.maxRedirects = Number(spec.maxRedirects) || 10; this.activeClass = spec.activeClass || 'active'; } _setupHooks(spec) { if (spec.beforeEach) { this.beforeEach = spec.beforeEach; } if (spec.afterEach) { this.afterEach = spec.afterEach; } } _setupState() { this.states = {}; this.context = { parent: null, state: null, // root state vm: null, params: {}, data: {} }; this.mountPoints = { '': { viewEl: this.el, viewElChildren: [].slice.call(this.el.children) } }; this.transition = null; } /** * Executed before `enter` hooks on each state. * * @returns {Promise} */ beforeEach() {} /** * Executed after `leave` hooks on each state. * * @returns {Promise} */ afterEach() {} /** * Handles errors uncaught during transition. * Used for overriding on a per-instance basis. * * @returns {Promise} */ handleUncaught(err) { return Promise.reject(err); } /** * Registers a new state with specified `name`. * * You can use one of two styles: * * ```es6 * sm.add('foo', { ... }); * sm.add({ name: 'foo', ... }); * ``` * * All options are passed to `State` constructor. * * @returns {State} */ add(name, spec) { if (typeof name == 'object') { spec = name; name = spec.name } if (!name) { throw new Error('State `name` is mandatory.') } if (this.states[name]) { throw new Error('State "' + name + '" already added'); } spec.name = name; const state = new State(this, spec); debug('add %s', name); this.states[name] = state; return state; } /** * Retrieves a state previously registered via `add`. * * @returns {State} */ get(name) { return this.states[name]; } /** * Navigates to a state with specified `name`. * Navigation is performed asynchronously, allowing * state enter/leave hook to perform async tasks * (e.g. fetch data). * * Throws an exception if another transition is * taking place. * * Options: * * * `name` — target state name (if null, will transition to same state * with updated params) * * `params` — object containing state parameters * (either path variables or query string parameters) * * `replace` — if `true` don't create a separate record * in browser history (default is `false`) * * Transition process: * * * find nearest common ancestor state with matching parameters * * go "upstream", leaving states, cleaning up states, * destroying components * * go "downstream", entering states, instantiating and rendering * components * * update browser history to reflect target state * (e.g. set new URL in address bar) * * Note that it is the responsibility of the state to compute * target URL. * * @returns {Promise} resolved when navigation is finished. */ go(options) { if (this.transition) { throw new Error('Transition is in progress.') } this.transition = new Transition(this); const currentState = this.context.state; let name = null; if (typeof options === 'string') { name = options; options = {}; } else if (options.name) { name = options.name; } else if (currentState) { name = currentState.name; } else { throw new Error('Destination state not specified'); } return Promise.resolve() .then(() => this.transition.go(name, options.params || {})) .then(result => { this.transition = null; this.emit('transition_finished'); return result; }) .catch(err => { this.transition = null; this.emit('transition_finished', err); return this.handleUncaught(err); }) .then(() => this._updateHistory(options.replace || false)); } /** * Updates browser history without actually performing * any transitions. * * Typically used to serialize parameters into query string * without performing navigation (e.g. when params are used * only in Vue components). */ update(params, replace) { Object.assign(this.context.params, params); this.emit('context_updated', this.context); return Promise.resolve() .then(() => this._updateHistory(replace)); } /** * Resolves nearest mount point in current context tree. * * Mount point is a "slot" that corresponds to `v-view` directive * (and the component that hosts it). */ _getMountPoint() { let ctx = this.context; let el = null; while (ctx && !el) { const state = ctx.state; if (state) { el = this.mountPoints[state.name]; } else { el = this.mountPoints['']; } ctx = ctx.parent; } return el; } /** * Begins listening for history events (e.g. browser back button) * and performs initial navigation by matching current URL. * * @returns {Promise} resolved when initial navigation is finished. */ start() { if (this._unlisten) { return Promise.resolve(); } this._matchLocation(window.location) this._unlisten = this.history.listen((location, action) => {this._matchLocation(location)}); return new Promise(resolve => this.once('history_updated', resolve)); } /** * Stops listening for history events. */ stop() { if (!this._unlisten) { return; } this._unlisten(); delete this._unlisten; } _matchLocation(location) { const url = location.pathname + location.search; if (url === this.context.url) { return; } const found = Object.keys(this.states).find(name => { const state = this.states[name]; const matched = state._match(location); if (matched) { debug('match url %s -> %s', location.pathname, name); this.go({ name: name, params: matched, replace: true }); return true; } }); if (!found) { /* eslint-disable no-console */ console.warn('No states match URL: ' + location.pathname); /* eslint-enable no-console */ this._updateHistory(true); } } _updateHistory(replace) { const state = this.context.state; const url = state ? state._makeUrl(this.context.params) : '/'; if (url === this.context.url) { return; } this.context.url = url; if (replace) { this.history.replace(url); } else { this.history.push(url); } this.emit('history_updated', { url: url, ctx: this.context }); } }; ================================================ FILE: src/state.js ================================================ import pathToRegexp from 'path-to-regexp'; import querystring from 'query-string'; /** * Represents a node in application states hierarchy. * * Most state features are optional, the only required * setting is a unique `name` and a reference to `parent` state. * * Depending on features a state can represent: * * * an abstract view (e.g. layout containing a component with `` * directive) * * a concrete view * * a route with path pattern and query parameters * * a data resolver that provides data to its descendants * * a mix of above * * Invoking constructor directly is not recommended, * use `StateManager#add` instead. * * @private */ export default class State { /** * Options: * * * `name` — required, state name; if contains dots `.` * then parent state will be inferred from this name * (e.g. `layout.users` will become a parent of `layout.users.list`) * unless `parent` is specified explicitly * * * `parent` — optional, parent state (this option disables * parent-from-name inference) * * * `path` — optional pathname pattern for matching URLs and * extracting parameters (e.g. `/user/:userId`). If started with `/`, * then path is treated as an absolute pathname, otherwise * path is treated as a fragment (absolute pathname is inherited * from parent) * * * `params` — optional object that specifies default values for * additional params that this state accepts; these params * will be encoded into query string * * * `component` — optional Vue component to be rendered * when entering this state * * * `redirect` — optional state name to redirect when navigating * to this state; typically used in "abstract" states (e.g. layouts, * data-preparation, etc.) to specify default sub-state * * * `enter` — optional `function(ctx, transition) => Promise` hook invoked * when transitioning into this state or its descendants * * * `leave` — optional `function(ctx, transition) => Promise` hook invoked * when transitioning from this state or its descendants * * * `handleError` — optional `function(err, ctx) => Promise` invoked * when error occurs during transition into/over this state * * @param manager * @param spec */ constructor(manager, spec) { this.manager = manager; this._setupName(spec); this._setupHierarchy(spec); this._setupComponent(spec); this._setupHooks(spec); this._setupPath(spec); this._setupParams(spec); this._setupOptions(spec); } _setupName(spec) { this.name = spec.name; if (!this.name) { throw new Error('State `name` is required'); } } _setupHierarchy(spec) { this.parentState = this.manager.get(spec.parent || this.name.split('.').slice(0, -1).join('.')) || null; this.lineage = this.parentState ? this.parentState.lineage.concat([this]) : [this]; } _setupComponent(spec) { if (spec.component) { this.component = spec.component; if (!this.component.name) { this.component.name = this.name.replace(/\./g, '-'); } } } _setupHooks(spec) { if (spec.enter) { this.enter = spec.enter; } if (spec.leave) { this.leave = spec.leave; } if (spec.handleError) { this.handleError = spec.handleError; } } _setupPath(spec) { if (!spec.path && spec.url) { /* eslint-disable no-console */ console.warn('state.url is deprecated; use state.path instead'); /* eslint-enable no-console */ spec.path = spec.url; } this.path = spec.path || ''; if (this.path.indexOf('/') === 0) { this.fullPath = this.path; } else { const parentPath = this.parentState ? this.parentState.fullPath : '/'; this.fullPath = parentPath.replace(/\/+$/, '') + (this.path ? '/' + this.path : ''); } if (!this.fullPath) { this.fullPath = '/'; } this._pathParams = []; this._pathRegex = pathToRegexp(this.fullPath, this._pathParams); this._pathFormat = pathToRegexp.compile(this.fullPath); } _setupParams(spec) { this._paramsSpec = {}; this._pathParams.forEach(param => { this._paramsSpec[param.name] = null; }); Object.assign(this._paramsSpec, spec.params); } _setupOptions(spec) { if (spec.redirect) { this.redirect = spec.redirect; } } /** * Invoked when going "downstream" (either into this state or * through this state to one of its descendants). * * Primary usage is to process `ctx.params` and populate `ctx.data`. * * Can redirect to another state by resolving `{ redirect: anotherStateName }` * (e.g. for authentication). * * It is also possible to render a custom Vue component instead * by resolving `{ component: customVueComponent }`. * * @param ctx — object `{ state, params, data }` * @returns {Promise} */ enter(ctx) { return Promise.resolve(); } /** * Invoked when going "upstream" (either from this state or * through this state from one of its descendants). * Typically used from cleaning up stuff used by this state. * * Like with `enter`, it is possible to redirect to another state * by resolving `{ redirect: anotherStateName }`. * * @param ctx — object `{ state, params, data }` * @returns {Promise} */ leave(ctx) { return Promise.resolve(); } /** * Invoked when error occurs during entering this state. * * Can redirect to another state by resolving `{ redirect: anotherStateName }` * (e.g. for authentication). * * It is also possible to render a custom Vue component instead * by resolving `{ component: customVueComponent }`. * * @param ctx — object `{ state, params, data }` * @returns {Promise} */ handleError(err, ctx) { return Promise.reject(err); } /** * Returns true if this state is equal to `stateOrName` * or contains `stateOrName` somewhere up the hierarchy. * * @param {string|State} stateOrName * @return {boolean} */ includes(stateOrName) { const state = stateOrName instanceof State ? stateOrName : this.manager.get(stateOrName); return this.lineage.indexOf(state) > -1; } /** * Attempts to match `location` to this state's `path` pattern. * * @param {{ pathname, search }} location * @returns an object with extracted params or `null` if don't match. * @private */ _match(location) { const matched = this._pathRegex.exec(location.pathname); if (!matched) { return null; } const params = this._pathParams.reduce((params, p, i) => { params[p.name] = matched[i + 1]; return params; }, {}); try { const query = querystring.parse(location.search); Object.assign(params, query); } catch (e) { // No query string or parsing failed — we don't care } return params; } /** * Constructs a `params` object by dropping any parameters * not specified in `_paramsSpec` of this state. * Values from `_paramsSpec` act as defaults. * * @param {object} params * @private */ _makeParams(params) { return Object.keys(this._paramsSpec).reduce((result, name) => { result[name] = name in params ? params[name] : this._paramsSpec[name]; return result; }, {}); } /** * Constructs search string by serializing query params. * * @param params * @return {string} search * @private */ _makeSearch(params) { const query = Object.keys(params).reduce((query, key) => { const value = params[key]; if (value != null) { query[key] = value; } return query; }, {}); this._pathParams.forEach(p => { delete query[p.name]; }); try { const search = querystring.stringify(query); if (search) { return '?' + search; } } catch (e) { // No query string or serialization failed — we don't care } return ''; } /** * Constructs an URL by encoding `params` into URL pattern and query string. * * Note: params not mentioned in `_paramsSpec` are dropped. * * @param params * @return {string} url * @private */ _makeUrl(params) { return this._pathFormat(params) + this._makeSearch(params); } /** * Creates href suitable for links (taking into account base URL and * hash-based histories). * * @param params * @return {string} href */ createHref(params) { return this.manager.history.createHref(this._makeUrl(params)); } }; ================================================ FILE: src/transition.js ================================================ import Debug from 'debug'; import { StateNotFoundError, RedirectLoopError } from './error'; import { toVueComponent } from './utils'; const debug = Debug('voie:transition'); export default class Transition { constructor(manager) { this.manager = manager; this.redirectsCount = 0; this.params = Object.assign({}, manager.context.params); } get to() { return this.dstState; } go(name, params, isRedirect) { debug(isRedirect ? 'redirect to %s' : 'go to %s', name); Object.assign(this.params, params || {}); const state = this.manager.get(name); if (!state) { throw new StateNotFoundError(name); } this.dstState = state; if (state.redirect) { return this.handleRedirect(state.redirect); } return this.goUpstream() .then(() => this.goDownstream()); } handleRedirect(redirect) { this.redirectsCount++; if (this.redirectsCount > this.manager.maxRedirects) { throw new RedirectLoopError(this); } switch (typeof redirect) { case 'string': return this.go(redirect, {}, true); case 'object': return this.go(redirect.name, redirect.params, true); case 'function': return Promise.resolve() .then(() => redirect(this)) .then(redirect => this.handleRedirect(redirect)); default: throw new Error('Unknown redirect: ' + redirect); } } goUpstream() { const ctx = this.manager.context; if (!ctx.state) { // We're at root state, no cleanup is necessary return Promise.resolve(); } // Stop going up if state is common with dst branch const state = ctx.state; if (this.dstState.includes(state)) { // All ctx params must match target ones // (e.g. when going from /user/1 to /user/2) const paramsMatch = Object.keys(ctx.params) .every(key => ctx.params[key] === this.params[key]); if (paramsMatch) { return Promise.resolve(); } } return Promise.resolve() .then(() => state.leave(ctx)) .then(() => this.manager.afterEach(ctx)) .then(() => { debug(' <- left %s', state.name); this.cleanup(ctx); }) .then(() => this.goUpstream()); } cleanup(ctx) { if (ctx.vm) { // Destroy vm and restore v-view element const el = ctx.vm.$el; const mp = ctx.mountPoint; ctx.vm.$destroy(); if (mp) { const viewEl = ctx.mountPoint.viewEl; el.parentNode.replaceChild(viewEl, el); mp.viewElChildren.forEach(el => viewEl.appendChild(el)); } } this.manager.context = ctx.parent; this.manager.emit('context_updated', this.manager.context); } goDownstream() { const prevCtx = this.manager.context; const dstLineage = this.dstState.lineage; const nextState = dstLineage[dstLineage.indexOf(prevCtx.state) + 1]; if (!nextState) { return Promise.resolve(); } // New context inherits params and data from parent const nextContext = { parent: prevCtx, state: nextState, params: Object.assign({}, prevCtx.params, nextState._makeParams(this.params)), data: Object.assign({}, prevCtx.data) }; return Promise.resolve(true) .then(() => this.manager.beforeEach(nextContext)) .catch(err => nextState.handleError(err, nextContext)) .then(obj => this._handleEnterHook(obj, nextContext)) .then(proceed => { if (!proceed) { return false; } return Promise.resolve() .then(() => nextState.enter(nextContext)) .catch(err => nextState.handleError(err, nextContext)) .then(obj => this._handleEnterHook(obj, nextContext)); }) .then(proceed => { if (!proceed) { return false; } this.manager.context = nextContext; this.manager.emit('context_updated', this.manager.context); this.render(nextContext, nextState.component); if (nextState !== this.dstState) { return this.goDownstream(); } }); } /** * @return {Boolean} proceed * @private */ _handleEnterHook(obj, nextContext) { obj = obj || {}; const nextState = nextContext.state; debug(' -> entered %s', nextState.name); // hooks can return { redirect: 'new.state.name' } // or { redirect: { name, params } } if (obj.redirect) { return this.handleRedirect(obj.redirect) .then(() => false); } // hooks can also return { component: } const rendered = this.render(nextContext, obj.component); return !rendered; } render(ctx, comp) { if (!comp) { return false; } const Comp = toVueComponent(comp); const mp = this.manager._getMountPoint(); ctx.mountPoint = mp; ctx.vm = new Comp({ data: ctx.data, el: mp.viewEl, parent: mp.hostVm, params: ctx.params, ctx: ctx, state: ctx.state, manager: this.manager }); return true; } } ================================================ FILE: src/utils.js ================================================ import Vue from 'vue'; export function toVueComponent(obj) { if (obj.name === 'VueComponent') { return obj } return Vue.extend(obj); } ================================================ FILE: test/.eslintrc ================================================ { "globals": { "describe": false, "context": false, "it": false, "before": false, "beforeEach": false, "after": false, "afterEach": false, "assert": false } } ================================================ FILE: test/karma.dev.conf.js ================================================ 'use strict'; module.exports = function(config) { config.set({ basePath: '..', port: 9876, files: [ 'src/**/*.js', 'test/specs/**/*.js' ], autoWatch: true, frameworks: ['mocha', 'chai', 'browserify'], preprocessors: { 'src/**/*.js': ['browserify'], 'test/specs/**/*.js': ['browserify'] }, browserify: { debug: true, transform: [ [ 'babelify', { presets: [ 'es2015' ] } ] ] }, reporters: ['spec'], client: { mocha: { reporter: 'html' } }, colors: true, logLevel: config.LOG_INFO, browsers: ['Chrome'], singleRun: false, concurrency: Infinity }); }; ================================================ FILE: test/specs/query.js ================================================ import StateManager from '../../src/state-manager'; import { createHashHistory } from 'history'; describe('Query support', function() { beforeEach(() => { const root = document.createElement('div'); root.setAttribute('id', 'root'); document.body.appendChild(root); }); afterEach(() => { const root = document.getElementById('root'); document.body.removeChild(root); // We use hash history here to simplify test infrastructure location.hash = ''; }); it('should format URL with query strings', function() { const sm = createStateManager(); const qs = sm.get('user')._makeUrl({ userName: 'Alice', collapsed: true, tags: ['one', 'two'] }); assert.equal(qs, '/user/Alice?collapsed=true&tags=one&tags=two'); }); it('should inherit default query params from hierarchy', function() { const sm = createStateManager(); return sm.go({ name: 'user', params: { userName: 'Alice' } }).then(() => { assert.equal(sm.context.url, '/user/Alice?collapsed=false'); }); }); it('should override both inherited and own params', function() { const sm = createStateManager(); return sm.go({ name: 'user', params: { userName: 'Alice', section: 'some', collapsed: true } }).then(() => { assert.equal(sm.context.url, '/user/Alice?collapsed=true§ion=some'); }); }); it('should drop nulls in query params', function() { const sm = createStateManager(); return sm.go({ name: 'user', params: { userName: 'Alice', section: null, collapsed: null } }).then(() => { assert.equal(sm.context.url, '/user/Alice'); }); }); it('should parse query params from location', function() { const sm = createStateManager(); location.hash = '#/user/Alice?collapsed=true§ion=any&tags=foo&tags=bar'; return sm.start().then(() => { const ctx = sm.context; assert.equal(ctx.params.collapsed, 'true'); assert.equal(ctx.params.section, 'any'); assert.lengthOf(ctx.params.tags, 2); }) .then(() => sm.stop()); }); it('should update history', function() { const sm = createStateManager(); return sm.go({ name: 'user', params: { userName: 'Alice' } }).then(() => sm.update({ collapsed: true })) .then(() => { assert.equal(location.hash, '#/user/Alice?collapsed=true') }); }); function createStateManager() { const sm = new StateManager({ el: '#root', history: createHashHistory({ queryKey: false }) }); sm.add({ name: 'user', redirect: 'user.info', path: '/user/:userName', params: { collapsed: false, tags: [] } }); sm.add({ name: 'user.info', params: { section: null } }); return sm; } }); ================================================ FILE: test/specs/routing.js ================================================ import StateManager from '../../src/state-manager'; import { createHashHistory } from 'history'; describe('URL routing', function() { beforeEach(() => { const root = document.createElement('div'); root.setAttribute('id', 'root'); document.body.appendChild(root); }); afterEach(() => { const root = document.getElementById('root'); document.body.removeChild(root); // We use hash history here to simplify test infrastructure location.hash = ''; }); it('should register URL patterns', function() { const sm = createStateManager(); assert.equal(sm.get('app').fullPath, '/'); assert.equal(sm.get('users').fullPath, '/users'); assert.equal(sm.get('users.list').fullPath, '/users/list'); assert.equal(sm.get('user').fullPath, '/user/:userName'); assert.equal(sm.get('user.dashboard').fullPath, '/user/:userName'); assert.equal(sm.get('user.messages').fullPath, '/user/:userName/messages'); }); it('should register named URL params', function() { const sm = createStateManager(); assert.lengthOf(sm.get('users')._pathParams, 0); assert.lengthOf(sm.get('user')._pathParams, 1); assert.equal(sm.get('user')._pathParams[0].name, 'userName'); }); it('should match simple URLs', function() { const sm = createStateManager(); assert.ok(sm.get('app')._match({ pathname: '/' })); assert.ok(sm.get('app')._match({ pathname: '' })); assert.notOk(sm.get('app')._match({ pathname: '/users' })); assert.ok(sm.get('users')._match({ pathname: '/users' })); assert.ok(sm.get('users')._match({ pathname: '/users/' })); assert.notOk(sm.get('users')._match({ pathname: '/users/list' })); }); it('should match URLs with parameters', function() { const sm = createStateManager(); assert.notOk(sm.get('user')._match({ pathname: '/user/' })); assert.ok(sm.get('user')._match({ pathname: '/user/Jane' })); }); it('should extract named params from URL', function() { const sm = createStateManager(); const st = sm.get('user.messages'); assert.equal(st._match({ pathname: '/user/Alice/messages' }).userName, 'Alice'); }); it('should format URLs with parameters', function() { const sm = createStateManager(); const st = sm.get('user.messages'); assert.equal(st._pathFormat({ userName: 'Alice' }), '/user/Alice/messages'); }); it('should visit root state automatically after start', function() { const sm = createStateManager(); return sm.start() .then(() => { assert.equal(sm.context.state.name, 'users.list'); }) .then(() => sm.stop()); }); it('should update URL after start', function() { const sm = createStateManager(); return sm.start() .then(() => { assert.equal(location.hash, '#/users/list'); }) .then(() => sm.stop()); }); it('should update URL after visiting another state', function() { const sm = createStateManager(); return sm.start() .then(() => sm.go({ name: 'user.messages', params: { userName: 'Alice' } })) .then(() => { assert.equal(location.hash, '#/user/Alice/messages'); }) .then(() => sm.stop()); }); it('should update URL after visiting same state with different params', function() { const sm = createStateManager(); return sm.start() .then(() => sm.go({ name: 'user', params: { userName: 'Alice' } })) .then(() => { assert.equal(sm.context.state.name, 'user.dashboard'); assert.equal(location.hash, '#/user/Alice'); }) .then(() => sm.go({ name: 'user', params: { userName: 'Bob' } })) .then(() => { assert.equal(sm.context.state.name, 'user.dashboard'); assert.equal(location.hash, '#/user/Bob'); }) .then(() => sm.stop()); }); it('should support optional URL params with defaults', function() { const sm = createStateManager(); return sm.start() .then(() => sm.go({ name: 'hello' })) .then(() => { assert.equal(sm.context.state.name, 'hello'); assert.equal(location.hash, '#/hello/World'); assert.equal(sm.context.params.name, 'World'); }) .then(() => sm.go({ name: 'hello', params: { name: 'Alice' } })) .then(() => { assert.equal(sm.context.state.name, 'hello'); assert.equal(location.hash, '#/hello/Alice'); assert.equal(sm.context.params.name, 'Alice'); }) .then(() => sm.stop()); }); function createStateManager() { const sm = new StateManager({ el: '#root', history: createHashHistory({ queryKey: false }) }); sm.add({ name: 'app', redirect: 'users' }); sm.add({ name: 'users', path: '/users', parent: 'app', redirect: 'users.list' }); sm.add({ name: 'users.list', path: 'list' }); sm.add({ name: 'user', parent: 'users', redirect: 'user.dashboard', path: '/user/:userName' }); sm.add({ name: 'user.dashboard' }); sm.add({ name: 'user.messages', path: 'messages' }); sm.add({ name: 'hello', path: '/hello/:name?', params: { name: 'World' } }); return sm; } }); ================================================ FILE: test/specs/states.js ================================================ import StateManager from '../../src/state-manager'; import { createMemoryHistory } from 'history'; describe('States', function() { describe('hierarchy', function() { const sm = new StateManager({ el: document.body, history: createMemoryHistory() }); sm.add({ name: 'app' }); sm.add({ name: 'app.welcome' }); sm.add({ name: 'app.welcome.hello' }); sm.add({ name: 'users', parent: 'app' }); sm.add({ name: 'users.list' }); sm.add({ name: 'groups', parent: 'app' }); it('state manager should register states', function() { assert.ok(sm.get('app')); assert.ok(sm.get('app.welcome')); assert.notOk(sm.get('app.wut')); }); it('parents are inferred from names', function() { assert.equal(sm.get('app.welcome').parentState, sm.get('app')); assert.equal(sm.get('app.welcome.hello').parentState, sm.get('app.welcome')); }); it('parent states can be specified explicitly', function() { assert.equal(sm.get('users').parentState, sm.get('app')); assert.equal(sm.get('groups').parentState, sm.get('app')); }); it('lineage shows upstream path from state to root', function() { const usersList = sm.get('users.list'); const groups = sm.get('groups'); assert.lengthOf(usersList.lineage, 3); assert.equal(usersList.lineage[0], sm.get('app')); assert.equal(usersList.lineage[1], sm.get('users')); assert.equal(usersList.lineage[2], usersList); assert.lengthOf(groups.lineage, 2); assert.equal(groups.lineage[0], sm.get('app')); assert.equal(groups.lineage[1], groups); }); it('should detect included state (check if self or ancestor)', function() { assert.ok(sm.get('users.list').includes('users')); assert.notOk(sm.get('users.list').includes('groups')); // By state assert.ok(sm.get('users').includes(sm.get('app'))); }); }); describe('params processing', function() { const sm = new StateManager({ el: document.body, history: createMemoryHistory() }); sm.add({ name: 'user', params: { name: null, display: 'short', greeting: 'Hello' } }); it('should apply param defaults from spec', function() { const params = sm.get('user')._makeParams({ name: 'Alice', display: 'full' }); assert.equal(params.name, 'Alice'); // overridden assert.equal(params.display, 'full'); // overridden assert.equal(params.greeting, 'Hello'); // default }); it('should drop param not from spec', function() { const params = sm.get('user')._makeParams({ name: 'Alice', something: '?' }); assert.notOk(params.something); }); }); }); ================================================ FILE: test/specs/transitions.js ================================================ import StateManager from '../../src/state-manager'; import { createMemoryHistory } from 'history'; describe('Transitions', function() { beforeEach(() => { const root = document.createElement('div'); root.setAttribute('id', 'root'); document.body.appendChild(root); }); afterEach(() => { const root = document.getElementById('root'); document.body.removeChild(root); }); it('should leave upstream and enter downstream states', function() { const sm = createStateManager(); const entered = []; const left = []; ['app', 'users', 'users.list', 'groups', 'groups.list'].forEach(name => { const state = sm.get(name); const e = state.enter; const l = state.leave; state.enter = (ctx) => { entered.push(state.name); return e(ctx); }; state.leave = (ctx) => { left.push(state.name); return l(ctx) }; }); return sm.go('users.list') .then(() => { assert.include(entered, 'app'); assert.include(entered, 'users'); assert.include(entered, 'users.list'); assert.notInclude(entered, 'groups'); assert.notInclude(entered, 'groups.list'); assert.notInclude(left, 'users.list'); assert.notInclude(left, 'users'); return sm.go('groups.list'); }) .then(() => { assert.include(left, 'users.list'); assert.include(left, 'users'); assert.notInclude(left, 'app'); assert.notInclude(left, 'groups'); assert.notInclude(left, 'groups.list'); assert.include(entered, 'groups'); assert.include(entered, 'groups.list'); }); }); it('should allow visiting redirect-only states', function() { const sm = createStateManager(); return sm.go('users') .then(() => { assert.equal(sm.context.state.name, 'users.list'); }); }); it('should redirect with params', function() { const sm = createStateManager(); return sm.go('groups') .then(() => { assert.equal(sm.context.state.name, 'groups.list'); assert.equal(sm.context.params.sort, '+name'); }); }); it('should load state data and render component in layout hierarchy', function() { const sm = createStateManager(); return sm.go('users') .then(() => { assert.equal(document.querySelector('#root h1').innerText, 'Users'); assert.lengthOf(document.querySelectorAll('#root li'), 3); assert.equal(document.querySelector('#root li:first-child').innerText, 'Alice'); }); }); it('should dispose of stale components and render new data', function() { const sm = createStateManager(); return sm.go('users') .then(() => sm.go('groups')) .then(() => { assert.lengthOf(document.querySelectorAll('#root h1'), 0); assert.lengthOf(document.querySelectorAll('#root li'), 2); assert.equal(document.querySelector('#root li:first-child').innerText, 'Admins'); }); }); it('should support redirect via state.enter hook', function() { const sm = createStateManager(); sm.get('groups.list').enter = () => ({ redirect: 'users' }); return sm.go('groups') .then(() => { assert.equal(sm.context.state.name, 'users.list'); }); }); it('should support rendering component via state.enter hook', function() { const sm = createStateManager(); sm.get('groups.list').enter = () => ({ component: { template: '

Groups

' } }); return sm.go('groups') .then(() => { assert.equal(document.querySelector('h2#root').innerText, 'Groups'); }); }); it('should detect redirect loops', function(done) { const sm = createStateManager(); sm.get('groups.list').enter = () => ({ redirect: 'users' }); sm.get('users.list').enter = () => ({ redirect: 'groups' }); sm.go('groups') .then(() => done(new Error('Redirect loop not detected.'))) .catch(err => { assert.isDefined(err.transition); done(); }); }); it('should handle uncaught errors ', function() { const sm = createStateManager(); let handled = null; sm.handleUncaught = function(err) { handled = err; }; sm.get('users.list').enter = () => { throw new Error('oopsie'); }; return sm.go('users.list') .then(() => { assert.ok(handled); assert.equal(handled.message, 'oopsie'); }); }); it('should allow redirecting on errors', function() { const sm = createStateManager(); let handled = false; sm.get('users.list').enter = () => { throw new Error('oopsie'); }; sm.get('users.list').handleError = () => { handled = true; return { redirect: 'groups.list' }; }; return sm.go('users.list') .then(() => { assert.equal(handled, true); assert.equal(sm.context.state.name, 'groups.list'); }); }); it('should render custom components on errors', function() { const sm = createStateManager(); let handled = false; sm.get('users.list').enter = () => { throw new Error('oopsie'); }; sm.get('users.list').handleError = () => { handled = true; return { component: { template: '

Error

' } }; }; return sm.go('users.list') .then(() => { assert.equal(handled, true); assert.equal(document.querySelector('#root h2').innerText, 'Error'); }); }); function createStateManager() { const sm = new StateManager({ el: '#root', history: createMemoryHistory() }); sm.add({ name: 'app' }); sm.add({ name: 'users', parent: 'app', redirect: 'users.list', component: { template: '

Users

' } }); sm.add({ name: 'users.list', enter: (ctx) => { ctx.data.users = [ { name: 'Alice' }, { name: 'Bob' }, { name: 'Greg' } ]; }, component: { template: '
  • {{ user.name }}
' } }); sm.add({ name: 'groups', parent: 'app', redirect: { name: 'groups.list', params: { sort: '+name' } }, enter: (ctx) => { ctx.data.groups = [ { name: 'Admins' }, { name: 'Guests'} ]; } }); sm.add({ name: 'groups.list', params: { sort: null }, component: { template: '
  • {{ group.name }}
' } }); return sm; } });