text: /**
    * vue-router v2.1.0
    * (c) 2016 Evan You
    * @license MIT
    */
  (function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
    (global.VueRouter = factory());
  }(this, (function () { '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) {
        "development" !== '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 <a> child and apply listener and href
        var a = findAnchor(this.$slots.default)
        if (a) {
          // in case the <a> 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 <a> 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 ("development" !== '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 ("development" !== '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 ("development" !== '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 ("development" !== '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 ("development" !== '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') {
        "development" !== '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 ("development" !== '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, cb) {
      var this$1 = this;
  
    var route = this.router.match(location, this.current)
    this.confirmTransition(route, function () {
      this$1.updateRoute(route)
      cb && cb(route)
      this$1.ensureURL()
    })
  };
  
  History.prototype.confirmTransition = function confirmTransition (route, cb) {
      var this$1 = this;
  
    var current = this.current
    if (isSameRoute(route, current)) {
      this.ensureURL()
      return
    }
  
    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 }
      hook(route, current, function (to) {
        if (to === false) {
          // next(false) -> abort navigation, ensure current URL
          this$1.ensureURL(true)
        } 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)
        } 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) {
          this$1.pending = null
          cb(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 <base> 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 ("development" !== '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) {
      var this$1 = this;
  
      History.call(this, router, base)
      window.addEventListener('hashchange', function () {
        this$1.onHashChange()
      })
  
      // 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:
        "development" !== '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;
  
    "development" !== '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) {
      history.transitionTo(getHash())
    }
  
    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
  
  if (inBrowser && window.Vue) {
    window.Vue.use(VueRouter)
  }
  
  return VueRouter;
  
  })));