/** * vue-router v2.1.1 * (c) 2016 Evan You * @license MIT */ 'use strict'; var View = { name: 'router-view', functional: true, props: { name: { type: String, default: 'default' } }, render: function render (h, ref) { var props = ref.props; var children = ref.children; var parent = ref.parent; var data = ref.data; data.routerView = true var route = parent.$route var cache = parent._routerViewCache || (parent._routerViewCache = {}) var depth = 0 var inactive = false while (parent) { if (parent.$vnode && parent.$vnode.data.routerView) { depth++ } if (parent._inactive) { inactive = true } parent = parent.$parent } data.routerViewDepth = depth var matched = route.matched[depth] if (!matched) { return h() } var name = props.name var component = inactive ? cache[name] : (cache[name] = matched.components[name]) if (!inactive) { var hooks = data.hook || (data.hook = {}) hooks.init = function (vnode) { matched.instances[name] = vnode.child } hooks.prepatch = function (oldVnode, vnode) { matched.instances[name] = vnode.child } hooks.destroy = function (vnode) { if (matched.instances[name] === vnode.child) { matched.instances[name] = undefined } } } return h(component, data, children) } } /* */ function assert (condition, message) { if (!condition) { throw new Error(("[vue-router] " + message)) } } function warn (condition, message) { if (!condition) { typeof console !== 'undefined' && console.warn(("[vue-router] " + message)) } } /* */ var encode = encodeURIComponent var decode = decodeURIComponent function resolveQuery ( query, extraQuery ) { if ( extraQuery === void 0 ) extraQuery = {}; if (query) { var parsedQuery try { parsedQuery = parseQuery(query) } catch (e) { process.env.NODE_ENV !== 'production' && warn(false, e.message) parsedQuery = {} } for (var key in extraQuery) { parsedQuery[key] = extraQuery[key] } return parsedQuery } else { return extraQuery } } function parseQuery (query) { var res = {} query = query.trim().replace(/^(\?|#|&)/, '') if (!query) { return res } query.split('&').forEach(function (param) { var parts = param.replace(/\+/g, ' ').split('=') var key = decode(parts.shift()) var val = parts.length > 0 ? decode(parts.join('=')) : null if (res[key] === undefined) { res[key] = val } else if (Array.isArray(res[key])) { res[key].push(val) } else { res[key] = [res[key], val] } }) return res } function stringifyQuery (obj) { var res = obj ? Object.keys(obj).map(function (key) { var val = obj[key] if (val === undefined) { return '' } if (val === null) { return encode(key) } if (Array.isArray(val)) { var result = [] val.slice().forEach(function (val2) { if (val2 === undefined) { return } if (val2 === null) { result.push(encode(key)) } else { result.push(encode(key) + '=' + encode(val2)) } }) return result.join('&') } return encode(key) + '=' + encode(val) }).filter(function (x) { return x.length > 0; }).join('&') : null return res ? ("?" + res) : '' } /* */ function createRoute ( record, location, redirectedFrom ) { var route = { name: location.name || (record && record.name), meta: (record && record.meta) || {}, path: location.path || '/', hash: location.hash || '', query: location.query || {}, params: location.params || {}, fullPath: getFullPath(location), matched: record ? formatMatch(record) : [] } if (redirectedFrom) { route.redirectedFrom = getFullPath(redirectedFrom) } return Object.freeze(route) } // the starting route that represents the initial state var START = createRoute(null, { path: '/' }) function formatMatch (record) { var res = [] while (record) { res.unshift(record) record = record.parent } return res } function getFullPath (ref) { var path = ref.path; var query = ref.query; if ( query === void 0 ) query = {}; var hash = ref.hash; if ( hash === void 0 ) hash = ''; return (path || '/') + stringifyQuery(query) + hash } var trailingSlashRE = /\/$/ function isSameRoute (a, b) { if (b === START) { return a === b } else if (!b) { return false } else if (a.path && b.path) { return ( a.path.replace(trailingSlashRE, '') === b.path.replace(trailingSlashRE, '') && a.hash === b.hash && isObjectEqual(a.query, b.query) ) } else if (a.name && b.name) { return ( a.name === b.name && a.hash === b.hash && isObjectEqual(a.query, b.query) && isObjectEqual(a.params, b.params) ) } else { return false } } function isObjectEqual (a, b) { if ( a === void 0 ) a = {}; if ( b === void 0 ) b = {}; var aKeys = Object.keys(a) var bKeys = Object.keys(b) if (aKeys.length !== bKeys.length) { return false } return aKeys.every(function (key) { return String(a[key]) === String(b[key]); }) } function isIncludedRoute (current, target) { return ( current.path.indexOf(target.path.replace(/\/$/, '')) === 0 && (!target.hash || current.hash === target.hash) && queryIncludes(current.query, target.query) ) } function queryIncludes (current, target) { for (var key in target) { if (!(key in current)) { return false } } return true } /* */ // work around weird flow bug var toTypes = [String, Object] var Link = { name: 'router-link', props: { to: { type: toTypes, required: true }, tag: { type: String, default: 'a' }, exact: Boolean, append: Boolean, replace: Boolean, activeClass: String, event: { type: [String, Array], default: 'click' } }, render: function render (h) { var this$1 = this; var router = this.$router var current = this.$route var ref = router.resolve(this.to, current, this.append); var normalizedTo = ref.normalizedTo; var resolved = ref.resolved; var href = ref.href; var classes = {} var activeClass = this.activeClass || router.options.linkActiveClass || 'router-link-active' var compareTarget = normalizedTo.path ? createRoute(null, normalizedTo) : resolved classes[activeClass] = this.exact ? isSameRoute(current, compareTarget) : isIncludedRoute(current, compareTarget) var handler = function (e) { if (guardEvent(e)) { if (this$1.replace) { router.replace(normalizedTo) } else { router.push(normalizedTo) } } } var on = { click: guardEvent } if (Array.isArray(this.event)) { this.event.forEach(function (e) { on[e] = handler }) } else { on[this.event] = handler } var data = { class: classes } if (this.tag === 'a') { data.on = on data.attrs = { href: href } } else { // find the first child and apply listener and href var a = findAnchor(this.$slots.default) if (a) { // in case the is a static node a.isStatic = false var extend = _Vue.util.extend var aData = a.data = extend({}, a.data) aData.on = on var aAttrs = a.data.attrs = extend({}, a.data.attrs) aAttrs.href = href } else { // doesn't have child, apply listener to self data.on = on } } return h(this.tag, data, this.$slots.default) } } function guardEvent (e) { // don't redirect with control keys /* istanbul ignore if */ if (e.metaKey || e.ctrlKey || e.shiftKey) { return } // don't redirect when preventDefault called /* istanbul ignore if */ if (e.defaultPrevented) { return } // don't redirect on right click /* istanbul ignore if */ if (e.button !== 0) { return } // don't redirect if `target="_blank"` /* istanbul ignore if */ var target = e.target.getAttribute('target') if (/\b_blank\b/i.test(target)) { return } e.preventDefault() return true } function findAnchor (children) { if (children) { var child for (var i = 0; i < children.length; i++) { child = children[i] if (child.tag === 'a') { return child } if (child.children && (child = findAnchor(child.children))) { return child } } } } var _Vue function install (Vue) { if (install.installed) { return } install.installed = true _Vue = Vue Object.defineProperty(Vue.prototype, '$router', { get: function get () { return this.$root._router } }) Object.defineProperty(Vue.prototype, '$route', { get: function get$1 () { return this.$root._route } }) Vue.mixin({ beforeCreate: function beforeCreate () { if (this.$options.router) { this._router = this.$options.router this._router.init(this) Vue.util.defineReactive(this, '_route', this._router.history.current) } } }) Vue.component('router-view', View) Vue.component('router-link', Link) var strats = Vue.config.optionMergeStrategies // use the same hook merging strategy for route hooks strats.beforeRouteEnter = strats.beforeRouteLeave = strats.created } /* */ function resolvePath ( relative, base, append ) { if (relative.charAt(0) === '/') { return relative } if (relative.charAt(0) === '?' || relative.charAt(0) === '#') { return base + relative } var stack = base.split('/') // remove trailing segment if: // - not appending // - appending to trailing slash (last segment is empty) if (!append || !stack[stack.length - 1]) { stack.pop() } // resolve relative path var segments = relative.replace(/^\//, '').split('/') for (var i = 0; i < segments.length; i++) { var segment = segments[i] if (segment === '.') { continue } else if (segment === '..') { stack.pop() } else { stack.push(segment) } } // ensure leading slash if (stack[0] !== '') { stack.unshift('') } return stack.join('/') } function parsePath (path) { var hash = '' var query = '' var hashIndex = path.indexOf('#') if (hashIndex >= 0) { hash = path.slice(hashIndex) path = path.slice(0, hashIndex) } var queryIndex = path.indexOf('?') if (queryIndex >= 0) { query = path.slice(queryIndex + 1) path = path.slice(0, queryIndex) } return { path: path, query: query, hash: hash } } function cleanPath (path) { return path.replace(/\/\//g, '/') } /* */ function createRouteMap (routes) { var pathMap = Object.create(null) var nameMap = Object.create(null) routes.forEach(function (route) { addRouteRecord(pathMap, nameMap, route) }) return { pathMap: pathMap, nameMap: nameMap } } function addRouteRecord ( pathMap, nameMap, route, parent, matchAs ) { var path = route.path; var name = route.name; if (process.env.NODE_ENV !== 'production') { assert(path != null, "\"path\" is required in a route configuration.") assert( typeof route.component !== 'string', "route config \"component\" for path: " + (String(path || name)) + " cannot be a " + "string id. Use an actual component instead." ) } var record = { path: normalizePath(path, parent), components: route.components || { default: route.component }, instances: {}, name: name, parent: parent, matchAs: matchAs, redirect: route.redirect, beforeEnter: route.beforeEnter, meta: route.meta || {} } if (route.children) { // Warn if route is named and has a default child route. // If users navigate to this route by name, the default child will // not be rendered (GH Issue #629) if (process.env.NODE_ENV !== 'production') { if (route.name && route.children.some(function (child) { return /^\/?$/.test(child.path); })) { warn(false, ("Named Route '" + (route.name) + "' has a default child route.\n When navigating to this named route (:to=\"{name: '" + (route.name) + "'\"), the default child route will not be rendered.\n Remove the name from this route and use the name of the default child route for named links instead.") ) } } route.children.forEach(function (child) { addRouteRecord(pathMap, nameMap, child, record) }) } if (route.alias !== undefined) { if (Array.isArray(route.alias)) { route.alias.forEach(function (alias) { addRouteRecord(pathMap, nameMap, { path: alias }, parent, record.path) }) } else { addRouteRecord(pathMap, nameMap, { path: route.alias }, parent, record.path) } } if (!pathMap[record.path]) { pathMap[record.path] = record } if (name) { if (!nameMap[name]) { nameMap[name] = record } else if (process.env.NODE_ENV !== 'production') { warn(false, ("Duplicate named routes definition: { name: \"" + name + "\", path: \"" + (record.path) + "\" }")) } } } function normalizePath (path, parent) { path = path.replace(/\/$/, '') if (path[0] === '/') { return path } if (parent == null) { return path } return cleanPath(((parent.path) + "/" + path)) } var __moduleExports = Array.isArray || function (arr) { return Object.prototype.toString.call(arr) == '[object Array]'; }; var isarray = __moduleExports /** * Expose `pathToRegexp`. */ var index = pathToRegexp var parse_1 = parse var compile_1 = compile var tokensToFunction_1 = tokensToFunction var tokensToRegExp_1 = 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 * @param {Object=} options * @return {!Array} */ function parse (str, options) { var tokens = [] var key = 0 var index = 0 var path = '' var defaultDelimiter = options && options.delimiter || '/' 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 } var next = str[index] var prefix = res[2] var name = res[3] var capture = res[4] var group = res[5] var modifier = res[6] var asterisk = res[7] // Push the current path onto the tokens. if (path) { tokens.push(path) path = '' } var partial = prefix != null && next != null && next !== prefix var repeat = modifier === '+' || modifier === '*' var optional = modifier === '?' || modifier === '*' var delimiter = res[2] || defaultDelimiter var pattern = capture || group tokens.push({ name: name || key++, prefix: prefix || '', delimiter: delimiter, optional: optional, repeat: repeat, partial: partial, asterisk: !!asterisk, pattern: pattern ? escapeGroup(pattern) : (asterisk ? '.*' : '[^' + escapeString(delimiter) + ']+?') }) } // 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 * @param {Object=} options * @return {!function(Object=, Object=)} */ function compile (str, options) { return tokensToFunction(parse(str, options)) } /** * Prettier encoding of URI path segments. * * @param {string} * @return {string} */ function encodeURIComponentPretty (str) { return encodeURI(str).replace(/[\/?#]/g, function (c) { return '%' + c.charCodeAt(0).toString(16).toUpperCase() }) } /** * Encode the asterisk parameter. Similar to `pretty`, but allows slashes. * * @param {string} * @return {string} */ function encodeAsterisk (str) { return encodeURI(str).replace(/[?#]/g, function (c) { return '%' + c.charCodeAt(0).toString(16).toUpperCase() }) } /** * 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, opts) { var path = '' var data = obj || {} var options = opts || {} var encode = options.pretty ? encodeURIComponentPretty : encodeURIComponent 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) { // Prepend partial segment prefixes. if (token.partial) { path += token.prefix } 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 `' + JSON.stringify(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 = encode(value[j]) if (!matches[i].test(segment)) { throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received `' + JSON.stringify(segment) + '`') } path += (j === 0 ? token.prefix : token.delimiter) + segment } continue } segment = token.asterisk ? encodeAsterisk(value) : encode(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, partial: false, asterisk: 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) { return tokensToRegExp(parse(path, options), keys, options) } /** * Expose a function for taking tokens and returning a RegExp. * * @param {!Array} tokens * @param {(Array|Object)=} keys * @param {Object=} options * @return {!RegExp} */ function tokensToRegExp (tokens, keys, options) { if (!isarray(keys)) { options = /** @type {!Object} */ (keys || options) keys = [] } options = options || {} var strict = options.strict var end = options.end !== false var route = '' // 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 + ')' keys.push(token) if (token.repeat) { capture += '(?:' + prefix + capture + ')*' } if (token.optional) { if (!token.partial) { capture = '(?:' + prefix + '(' + capture + '))?' } else { capture = prefix + '(' + capture + ')?' } } else { capture = prefix + '(' + capture + ')' } route += capture } } var delimiter = escapeString(options.delimiter || '/') var endsWithDelimiter = route.slice(-delimiter.length) === delimiter // 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 = (endsWithDelimiter ? route.slice(0, -delimiter.length) : route) + '(?:' + delimiter + '(?=$))?' } 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 && endsWithDelimiter ? '' : '(?=' + delimiter + '|$)' } return attachKeys(new RegExp('^' + route, flags(options)), keys) } /** * 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|Object)=} keys * @param {Object=} options * @return {!RegExp} */ function pathToRegexp (path, keys, options) { if (!isarray(keys)) { options = /** @type {!Object} */ (keys || options) keys = [] } options = options || {} if (path instanceof RegExp) { return regexpToRegexp(path, /** @type {!Array} */ (keys)) } if (isarray(path)) { return arrayToRegexp(/** @type {!Array} */ (path), /** @type {!Array} */ (keys), options) } return stringToRegexp(/** @type {string} */ (path), /** @type {!Array} */ (keys), options) } index.parse = parse_1; index.compile = compile_1; index.tokensToFunction = tokensToFunction_1; index.tokensToRegExp = tokensToRegExp_1; /* */ var regexpCache = Object.create(null) function getRouteRegex (path) { var hit = regexpCache[path] var keys, regexp if (hit) { keys = hit.keys regexp = hit.regexp } else { keys = [] regexp = index(path, keys) regexpCache[path] = { keys: keys, regexp: regexp } } return { keys: keys, regexp: regexp } } var regexpCompileCache = Object.create(null) function fillParams ( path, params, routeMsg ) { try { var filler = regexpCompileCache[path] || (regexpCompileCache[path] = index.compile(path)) return filler(params || {}, { pretty: true }) } catch (e) { if (process.env.NODE_ENV !== 'production') { warn(false, ("missing param for " + routeMsg + ": " + (e.message))) } return '' } } /* */ function normalizeLocation ( raw, current, append ) { var next = typeof raw === 'string' ? { path: raw } : raw // named target if (next.name || next._normalized) { return next } // relative params if (!next.path && next.params && current) { next = assign({}, next) next._normalized = true var params = assign(assign({}, current.params), next.params) if (current.name) { next.name = current.name next.params = params } else if (current.matched) { var rawPath = current.matched[current.matched.length - 1].path next.path = fillParams(rawPath, params, ("path " + (current.path))) } else if (process.env.NODE_ENV !== 'production') { warn(false, "relative params navigation requires a current route.") } return next } var parsedPath = parsePath(next.path || '') var basePath = (current && current.path) || '/' var path = parsedPath.path ? resolvePath(parsedPath.path, basePath, append || next.append) : (current && current.path) || '/' var query = resolveQuery(parsedPath.query, next.query) var hash = next.hash || parsedPath.hash if (hash && hash.charAt(0) !== '#') { hash = "#" + hash } return { _normalized: true, path: path, query: query, hash: hash } } function assign (a, b) { for (var key in b) { a[key] = b[key] } return a } /* */ function createMatcher (routes) { var ref = createRouteMap(routes); var pathMap = ref.pathMap; var nameMap = ref.nameMap; function match ( raw, currentRoute, redirectedFrom ) { var location = normalizeLocation(raw, currentRoute) var name = location.name; if (name) { var record = nameMap[name] var paramNames = getRouteRegex(record.path).keys .filter(function (key) { return !key.optional; }) .map(function (key) { return key.name; }) if (typeof location.params !== 'object') { location.params = {} } if (currentRoute && typeof currentRoute.params === 'object') { for (var key in currentRoute.params) { if (!(key in location.params) && paramNames.indexOf(key) > -1) { location.params[key] = currentRoute.params[key] } } } if (record) { location.path = fillParams(record.path, location.params, ("named route \"" + name + "\"")) return _createRoute(record, location, redirectedFrom) } } else if (location.path) { location.params = {} for (var path in pathMap) { if (matchRoute(path, location.params, location.path)) { return _createRoute(pathMap[path], location, redirectedFrom) } } } // no match return _createRoute(null, location) } function redirect ( record, location ) { var originalRedirect = record.redirect var redirect = typeof originalRedirect === 'function' ? originalRedirect(createRoute(record, location)) : originalRedirect if (typeof redirect === 'string') { redirect = { path: redirect } } if (!redirect || typeof redirect !== 'object') { process.env.NODE_ENV !== 'production' && warn( false, ("invalid redirect option: " + (JSON.stringify(redirect))) ) return _createRoute(null, location) } var re = redirect var name = re.name; var path = re.path; var query = location.query; var hash = location.hash; var params = location.params; query = re.hasOwnProperty('query') ? re.query : query hash = re.hasOwnProperty('hash') ? re.hash : hash params = re.hasOwnProperty('params') ? re.params : params if (name) { // resolved named direct var targetRecord = nameMap[name] if (process.env.NODE_ENV !== 'production') { assert(targetRecord, ("redirect failed: named route \"" + name + "\" not found.")) } return match({ _normalized: true, name: name, query: query, hash: hash, params: params }, undefined, location) } else if (path) { // 1. resolve relative redirect var rawPath = resolveRecordPath(path, record) // 2. resolve params var resolvedPath = fillParams(rawPath, params, ("redirect route with path \"" + rawPath + "\"")) // 3. rematch with existing query and hash return match({ _normalized: true, path: resolvedPath, query: query, hash: hash }, undefined, location) } else { warn(false, ("invalid redirect option: " + (JSON.stringify(redirect)))) return _createRoute(null, location) } } function alias ( record, location, matchAs ) { var aliasedPath = fillParams(matchAs, location.params, ("aliased route with path \"" + matchAs + "\"")) var aliasedMatch = match({ _normalized: true, path: aliasedPath }) if (aliasedMatch) { var matched = aliasedMatch.matched var aliasedRecord = matched[matched.length - 1] location.params = aliasedMatch.params return _createRoute(aliasedRecord, location) } return _createRoute(null, location) } function _createRoute ( record, location, redirectedFrom ) { if (record && record.redirect) { return redirect(record, redirectedFrom || location) } if (record && record.matchAs) { return alias(record, location, record.matchAs) } return createRoute(record, location, redirectedFrom) } return match } function matchRoute ( path, params, pathname ) { var ref = getRouteRegex(path); var regexp = ref.regexp; var keys = ref.keys; var m = pathname.match(regexp) if (!m) { return false } else if (!params) { return true } for (var i = 1, len = m.length; i < len; ++i) { var key = keys[i - 1] var val = typeof m[i] === 'string' ? decodeURIComponent(m[i]) : m[i] if (key) { params[key.name] = val } } return true } function resolveRecordPath (path, record) { return resolvePath(path, record.parent ? record.parent.path : '/', true) } /* */ var inBrowser = typeof window !== 'undefined' var supportsHistory = inBrowser && (function () { var ua = window.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 } return window.history && 'pushState' in window.history })() /* */ function runQueue (queue, fn, cb) { var step = function (index) { if (index >= queue.length) { cb() } else { if (queue[index]) { fn(queue[index], function () { step(index + 1) }) } else { step(index + 1) } } } step(0) } /* */ var History = function History (router, base) { this.router = router this.base = normalizeBase(base) // start with a route object that stands for "nowhere" this.current = START this.pending = null }; History.prototype.listen = function listen (cb) { this.cb = cb }; History.prototype.transitionTo = function transitionTo (location, onComplete, onAbort) { var this$1 = this; var route = this.router.match(location, this.current) this.confirmTransition(route, function () { this$1.updateRoute(route) onComplete && onComplete(route) this$1.ensureURL() }, onAbort) }; History.prototype.confirmTransition = function confirmTransition (route, onComplete, onAbort) { var this$1 = this; var current = this.current var abort = function () { onAbort && onAbort() } if (isSameRoute(route, current)) { this.ensureURL() return abort() } var ref = resolveQueue(this.current.matched, route.matched); var deactivated = ref.deactivated; var activated = ref.activated; var queue = [].concat( // in-component leave guards extractLeaveGuards(deactivated), // global before hooks this.router.beforeHooks, // enter guards activated.map(function (m) { return m.beforeEnter; }), // async components resolveAsyncComponents(activated) ) this.pending = route var iterator = function (hook, next) { if (this$1.pending !== route) { return abort() } hook(route, current, function (to) { if (to === false) { // next(false) -> abort navigation, ensure current URL this$1.ensureURL(true) abort() } else if (typeof to === 'string' || typeof to === 'object') { // next('/') or next({ path: '/' }) -> redirect (typeof to === 'object' && to.replace) ? this$1.replace(to) : this$1.push(to) abort() } else { // confirm transition and pass on the value next(to) } }) } runQueue(queue, iterator, function () { var postEnterCbs = [] var enterGuards = extractEnterGuards(activated, postEnterCbs, function () { return this$1.current === route }) // wait until async components are resolved before // extracting in-component enter guards runQueue(enterGuards, iterator, function () { if (this$1.pending !== route) { return abort() } this$1.pending = null onComplete(route) if (this$1.router.app) { this$1.router.app.$nextTick(function () { postEnterCbs.forEach(function (cb) { return cb(); }) }) } }) }) }; History.prototype.updateRoute = function updateRoute (route) { var prev = this.current this.current = route this.cb && this.cb(route) this.router.afterHooks.forEach(function (hook) { hook && hook(route, prev) }) }; function normalizeBase (base) { if (!base) { if (inBrowser) { // respect tag var baseEl = document.querySelector('base') base = baseEl ? baseEl.getAttribute('href') : '/' } else { base = '/' } } // make sure there's the starting slash if (base.charAt(0) !== '/') { base = '/' + base } // remove trailing slash return base.replace(/\/$/, '') } function resolveQueue ( current, next ) { var i var max = Math.max(current.length, next.length) for (i = 0; i < max; i++) { if (current[i] !== next[i]) { break } } return { activated: next.slice(i), deactivated: current.slice(i) } } function extractGuard ( def, key ) { if (typeof def !== 'function') { // extend now so that global mixins are applied. def = _Vue.extend(def) } return def.options[key] } function extractLeaveGuards (matched) { return flatten(flatMapComponents(matched, function (def, instance) { var guard = extractGuard(def, 'beforeRouteLeave') if (guard) { return Array.isArray(guard) ? guard.map(function (guard) { return wrapLeaveGuard(guard, instance); }) : wrapLeaveGuard(guard, instance) } }).reverse()) } function wrapLeaveGuard ( guard, instance ) { return function routeLeaveGuard () { return guard.apply(instance, arguments) } } function extractEnterGuards ( matched, cbs, isValid ) { return flatten(flatMapComponents(matched, function (def, _, match, key) { var guard = extractGuard(def, 'beforeRouteEnter') if (guard) { return Array.isArray(guard) ? guard.map(function (guard) { return wrapEnterGuard(guard, cbs, match, key, isValid); }) : wrapEnterGuard(guard, cbs, match, key, isValid) } })) } function wrapEnterGuard ( guard, cbs, match, key, isValid ) { return function routeEnterGuard (to, from, next) { return guard(to, from, function (cb) { next(cb) if (typeof cb === 'function') { cbs.push(function () { // #750 // if a router-view is wrapped with an out-in transition, // the instance may not have been registered at this time. // we will need to poll for registration until current route // is no longer valid. poll(cb, match.instances, key, isValid) }) } }) } } function poll ( cb, // somehow flow cannot infer this is a function instances, key, isValid ) { if (instances[key]) { cb(instances[key]) } else if (isValid()) { setTimeout(function () { poll(cb, instances, key, isValid) }, 16) } } function resolveAsyncComponents (matched) { return flatMapComponents(matched, function (def, _, match, key) { // if it's a function and doesn't have Vue options attached, // assume it's an async component resolve function. // we are not using Vue's default async resolving mechanism because // we want to halt the navigation until the incoming component has been // resolved. if (typeof def === 'function' && !def.options) { return function (to, from, next) { var resolve = function (resolvedDef) { match.components[key] = resolvedDef next() } var reject = function (reason) { warn(false, ("Failed to resolve async component " + key + ": " + reason)) next(false) } var res = def(resolve, reject) if (res && typeof res.then === 'function') { res.then(resolve, reject) } } } }) } function flatMapComponents ( matched, fn ) { return flatten(matched.map(function (m) { return Object.keys(m.components).map(function (key) { return fn( m.components[key], m.instances[key], m, key ); }) })) } function flatten (arr) { return Array.prototype.concat.apply([], arr) } /* */ var positionStore = Object.create(null) function saveScrollPosition (key) { if (!key) { return } positionStore[key] = { x: window.pageXOffset, y: window.pageYOffset } } function getScrollPosition (key) { if (!key) { return } return positionStore[key] } function getElementPosition (el) { var docRect = document.documentElement.getBoundingClientRect() var elRect = el.getBoundingClientRect() return { x: elRect.left - docRect.left, y: elRect.top - docRect.top } } function isValidPosition (obj) { return isNumber(obj.x) || isNumber(obj.y) } function normalizePosition (obj) { return { x: isNumber(obj.x) ? obj.x : window.pageXOffset, y: isNumber(obj.y) ? obj.y : window.pageYOffset } } function isNumber (v) { return typeof v === 'number' } /* */ var genKey = function () { return String(Date.now()); } var _key = genKey() var HTML5History = (function (History) { function HTML5History (router, base) { var this$1 = this; History.call(this, router, base) var expectScroll = router.options.scrollBehavior window.addEventListener('popstate', function (e) { _key = e.state && e.state.key var current = this$1.current this$1.transitionTo(getLocation(this$1.base), function (next) { if (expectScroll) { this$1.handleScroll(next, current, true) } }) }) if (expectScroll) { window.addEventListener('scroll', function () { saveScrollPosition(_key) }) } } if ( History ) HTML5History.__proto__ = History; HTML5History.prototype = Object.create( History && History.prototype ); HTML5History.prototype.constructor = HTML5History; HTML5History.prototype.go = function go (n) { window.history.go(n) }; HTML5History.prototype.push = function push (location) { var this$1 = this; var current = this.current this.transitionTo(location, function (route) { pushState(cleanPath(this$1.base + route.fullPath)) this$1.handleScroll(route, current, false) }) }; HTML5History.prototype.replace = function replace (location) { var this$1 = this; var current = this.current this.transitionTo(location, function (route) { replaceState(cleanPath(this$1.base + route.fullPath)) this$1.handleScroll(route, current, false) }) }; HTML5History.prototype.ensureURL = function ensureURL (push) { if (getLocation(this.base) !== this.current.fullPath) { var current = cleanPath(this.base + this.current.fullPath) push ? pushState(current) : replaceState(current) } }; HTML5History.prototype.handleScroll = function handleScroll (to, from, isPop) { var router = this.router if (!router.app) { return } var behavior = router.options.scrollBehavior if (!behavior) { return } if (process.env.NODE_ENV !== 'production') { assert(typeof behavior === 'function', "scrollBehavior must be a function") } // wait until re-render finishes before scrolling router.app.$nextTick(function () { var position = getScrollPosition(_key) var shouldScroll = behavior(to, from, isPop ? position : null) if (!shouldScroll) { return } var isObject = typeof shouldScroll === 'object' if (isObject && typeof shouldScroll.selector === 'string') { var el = document.querySelector(shouldScroll.selector) if (el) { position = getElementPosition(el) } else if (isValidPosition(shouldScroll)) { position = normalizePosition(shouldScroll) } } else if (isObject && isValidPosition(shouldScroll)) { position = normalizePosition(shouldScroll) } if (position) { window.scrollTo(position.x, position.y) } }) }; return HTML5History; }(History)); function getLocation (base) { var path = window.location.pathname if (base && path.indexOf(base) === 0) { path = path.slice(base.length) } return (path || '/') + window.location.search + window.location.hash } function pushState (url, replace) { // try...catch the pushState call to get around Safari // DOM Exception 18 where it limits to 100 pushState calls var history = window.history try { if (replace) { history.replaceState({ key: _key }, '', url) } else { _key = genKey() history.pushState({ key: _key }, '', url) } saveScrollPosition(_key) } catch (e) { window.location[replace ? 'assign' : 'replace'](url) } } function replaceState (url) { pushState(url, true) } /* */ var HashHistory = (function (History) { function HashHistory (router, base, fallback) { History.call(this, router, base) // check history fallback deeplinking if (fallback && this.checkFallback()) { return } ensureSlash() } if ( History ) HashHistory.__proto__ = History; HashHistory.prototype = Object.create( History && History.prototype ); HashHistory.prototype.constructor = HashHistory; HashHistory.prototype.checkFallback = function checkFallback () { var location = getLocation(this.base) if (!/^\/#/.test(location)) { window.location.replace( cleanPath(this.base + '/#' + location) ) return true } }; HashHistory.prototype.onHashChange = function onHashChange () { if (!ensureSlash()) { return } this.transitionTo(getHash(), function (route) { replaceHash(route.fullPath) }) }; HashHistory.prototype.push = function push (location) { this.transitionTo(location, function (route) { pushHash(route.fullPath) }) }; HashHistory.prototype.replace = function replace (location) { this.transitionTo(location, function (route) { replaceHash(route.fullPath) }) }; HashHistory.prototype.go = function go (n) { window.history.go(n) }; HashHistory.prototype.ensureURL = function ensureURL (push) { var current = this.current.fullPath if (getHash() !== current) { push ? pushHash(current) : replaceHash(current) } }; return HashHistory; }(History)); function ensureSlash () { var path = getHash() if (path.charAt(0) === '/') { return true } replaceHash('/' + path) return false } function getHash () { // We can't use window.location.hash here because it's not // consistent across browsers - Firefox will pre-decode it! var href = window.location.href var index = href.indexOf('#') return index === -1 ? '' : href.slice(index + 1) } function pushHash (path) { window.location.hash = path } function replaceHash (path) { var i = window.location.href.indexOf('#') window.location.replace( window.location.href.slice(0, i >= 0 ? i : 0) + '#' + path ) } /* */ var AbstractHistory = (function (History) { function AbstractHistory (router) { History.call(this, router) this.stack = [] this.index = -1 } if ( History ) AbstractHistory.__proto__ = History; AbstractHistory.prototype = Object.create( History && History.prototype ); AbstractHistory.prototype.constructor = AbstractHistory; AbstractHistory.prototype.push = function push (location) { var this$1 = this; this.transitionTo(location, function (route) { this$1.stack = this$1.stack.slice(0, this$1.index + 1).concat(route) this$1.index++ }) }; AbstractHistory.prototype.replace = function replace (location) { var this$1 = this; this.transitionTo(location, function (route) { this$1.stack = this$1.stack.slice(0, this$1.index).concat(route) }) }; AbstractHistory.prototype.go = function go (n) { var this$1 = this; var targetIndex = this.index + n if (targetIndex < 0 || targetIndex >= this.stack.length) { return } var route = this.stack[targetIndex] this.confirmTransition(route, function () { this$1.index = targetIndex this$1.updateRoute(route) }) }; AbstractHistory.prototype.ensureURL = function ensureURL () { // noop }; return AbstractHistory; }(History)); /* */ var VueRouter = function VueRouter (options) { if ( options === void 0 ) options = {}; this.app = null this.options = options this.beforeHooks = [] this.afterHooks = [] this.match = createMatcher(options.routes || []) var mode = options.mode || 'hash' this.fallback = mode === 'history' && !supportsHistory if (this.fallback) { mode = 'hash' } if (!inBrowser) { mode = 'abstract' } this.mode = mode switch (mode) { case 'history': this.history = new HTML5History(this, options.base) break case 'hash': this.history = new HashHistory(this, options.base, this.fallback) break case 'abstract': this.history = new AbstractHistory(this) break default: process.env.NODE_ENV !== 'production' && assert(false, ("invalid mode: " + mode)) } }; var prototypeAccessors = { currentRoute: {} }; prototypeAccessors.currentRoute.get = function () { return this.history && this.history.current }; VueRouter.prototype.init = function init (app /* Vue component instance */) { var this$1 = this; process.env.NODE_ENV !== 'production' && assert( install.installed, "not installed. Make sure to call `Vue.use(VueRouter)` " + "before creating root instance." ) this.app = app var history = this.history if (history instanceof HTML5History) { history.transitionTo(getLocation(history.base)) } else if (history instanceof HashHistory) { var setupHashListener = function () { window.addEventListener('hashchange', function () { history.onHashChange() }) } history.transitionTo(getHash(), setupHashListener, setupHashListener) } history.listen(function (route) { this$1.app._route = route }) }; VueRouter.prototype.beforeEach = function beforeEach (fn) { this.beforeHooks.push(fn) }; VueRouter.prototype.afterEach = function afterEach (fn) { this.afterHooks.push(fn) }; VueRouter.prototype.push = function push (location) { this.history.push(location) }; VueRouter.prototype.replace = function replace (location) { this.history.replace(location) }; VueRouter.prototype.go = function go (n) { this.history.go(n) }; VueRouter.prototype.back = function back () { this.go(-1) }; VueRouter.prototype.forward = function forward () { this.go(1) }; VueRouter.prototype.getMatchedComponents = function getMatchedComponents (to) { var route = to ? this.resolve(to).resolved : this.currentRoute if (!route) { return [] } return [].concat.apply([], route.matched.map(function (m) { return Object.keys(m.components).map(function (key) { return m.components[key] }) })) }; VueRouter.prototype.resolve = function resolve ( to, current, append ) { var normalizedTo = normalizeLocation(to, current || this.history.current, append) var resolved = this.match(normalizedTo, current) var fullPath = resolved.redirectedFrom || resolved.fullPath var base = this.history.base var href = createHref(base, fullPath, this.mode) return { normalizedTo: normalizedTo, resolved: resolved, href: href } }; Object.defineProperties( VueRouter.prototype, prototypeAccessors ); function createHref (base, fullPath, mode) { var path = mode === 'hash' ? '#' + fullPath : fullPath return base ? cleanPath(base + '/' + path) : path } VueRouter.install = install; VueRouter.version = 'v2.1.1' if (inBrowser && window.Vue) { window.Vue.use(VueRouter) } module.exports = VueRouter;