import { callWithAsyncErrorHandling, camelize, capitalize, computed, createRenderer, defineComponent, extend, h, hyphenate, includeBooleanAttr, inject, invokeArrayFns, isArray, isFunction, isModelListener, isOn, isSet, isSpecialBooleanAttr, isString, isSymbol, looseEqual, looseIndexOf, looseToNumber, nextTick, provide, reactive, ref, shallowReactive, shallowRef, unref, watch } from "./runtime-core.esm-bundler-Cyv4obHQ.js"; let policy = void 0; const tt = typeof window !== "undefined" && window.trustedTypes; if (tt) try { policy = /* @__PURE__ */ tt.createPolicy("vue", { createHTML: (val) => val }); } catch (e) {} const unsafeToTrustedHTML = policy ? (val) => policy.createHTML(val) : (val) => val; const svgNS = "http://www.w3.org/2000/svg"; const mathmlNS = "http://www.w3.org/1998/Math/MathML"; const doc = typeof document !== "undefined" ? document : null; const templateContainer = doc && /* @__PURE__ */ doc.createElement("template"); const nodeOps = { insert: (child, parent, anchor) => { parent.insertBefore(child, anchor || null); }, remove: (child) => { const parent = child.parentNode; if (parent) parent.removeChild(child); }, createElement: (tag, namespace, is, props) => { const el = namespace === "svg" ? doc.createElementNS(svgNS, tag) : namespace === "mathml" ? doc.createElementNS(mathmlNS, tag) : is ? doc.createElement(tag, { is }) : doc.createElement(tag); if (tag === "select" && props && props.multiple != null) el.setAttribute("multiple", props.multiple); return el; }, createText: (text) => doc.createTextNode(text), createComment: (text) => doc.createComment(text), setText: (node, text) => { node.nodeValue = text; }, setElementText: (el, text) => { el.textContent = text; }, parentNode: (node) => node.parentNode, nextSibling: (node) => node.nextSibling, querySelector: (selector) => doc.querySelector(selector), setScopeId(el, id) { el.setAttribute(id, ""); }, insertStaticContent(content, parent, anchor, namespace, start, end) { const before = anchor ? anchor.previousSibling : parent.lastChild; if (start && (start === end || start.nextSibling)) while (true) { parent.insertBefore(start.cloneNode(true), anchor); if (start === end || !(start = start.nextSibling)) break; } else { templateContainer.innerHTML = unsafeToTrustedHTML(namespace === "svg" ? `${content}` : namespace === "mathml" ? `${content}` : content); const template = templateContainer.content; if (namespace === "svg" || namespace === "mathml") { const wrapper = template.firstChild; while (wrapper.firstChild) template.appendChild(wrapper.firstChild); template.removeChild(wrapper); } parent.insertBefore(template, anchor); } return [before ? before.nextSibling : parent.firstChild, anchor ? anchor.previousSibling : parent.lastChild]; } }; const vtcKey = Symbol("_vtc"); function patchClass(el, value, isSVG) { const transitionClasses = el[vtcKey]; if (transitionClasses) value = (value ? [value, ...transitionClasses] : [...transitionClasses]).join(" "); if (value == null) el.removeAttribute("class"); else if (isSVG) el.setAttribute("class", value); else el.className = value; } const vShowOriginalDisplay = Symbol("_vod"); const vShowHidden = Symbol("_vsh"); const vShow = { beforeMount(el, { value }, { transition }) { el[vShowOriginalDisplay] = el.style.display === "none" ? "" : el.style.display; if (transition && value) transition.beforeEnter(el); else setDisplay(el, value); }, mounted(el, { value }, { transition }) { if (transition && value) transition.enter(el); }, updated(el, { value, oldValue }, { transition }) { if (!value === !oldValue) return; if (transition) if (value) { transition.beforeEnter(el); setDisplay(el, true); transition.enter(el); } else transition.leave(el, () => { setDisplay(el, false); }); else setDisplay(el, value); }, beforeUnmount(el, { value }) { setDisplay(el, value); } }; function setDisplay(el, value) { el.style.display = value ? el[vShowOriginalDisplay] : "none"; el[vShowHidden] = !value; } const CSS_VAR_TEXT = Symbol(""); const displayRE = /(^|;)\s*display\s*:/; function patchStyle(el, prev, next) { const style = el.style; const isCssString = isString(next); let hasControlledDisplay = false; if (next && !isCssString) { if (prev) if (!isString(prev)) { for (const key in prev) if (next[key] == null) setStyle(style, key, ""); } else for (const prevStyle of prev.split(";")) { const key = prevStyle.slice(0, prevStyle.indexOf(":")).trim(); if (next[key] == null) setStyle(style, key, ""); } for (const key in next) { if (key === "display") hasControlledDisplay = true; setStyle(style, key, next[key]); } } else if (isCssString) { if (prev !== next) { const cssVarText = style[CSS_VAR_TEXT]; if (cssVarText) next += ";" + cssVarText; style.cssText = next; hasControlledDisplay = displayRE.test(next); } } else if (prev) el.removeAttribute("style"); if (vShowOriginalDisplay in el) { el[vShowOriginalDisplay] = hasControlledDisplay ? style.display : ""; if (el[vShowHidden]) style.display = "none"; } } const importantRE = /\s*!important$/; function setStyle(style, name, val) { if (isArray(val)) val.forEach((v) => setStyle(style, name, v)); else { if (val == null) val = ""; if (name.startsWith("--")) style.setProperty(name, val); else { const prefixed = autoPrefix(style, name); if (importantRE.test(val)) style.setProperty(hyphenate(prefixed), val.replace(importantRE, ""), "important"); else style[prefixed] = val; } } } const prefixes = [ "Webkit", "Moz", "ms" ]; const prefixCache = {}; function autoPrefix(style, rawName) { const cached = prefixCache[rawName]; if (cached) return cached; let name = camelize(rawName); if (name !== "filter" && name in style) return prefixCache[rawName] = name; name = capitalize(name); for (let i = 0; i < prefixes.length; i++) { const prefixed = prefixes[i] + name; if (prefixed in style) return prefixCache[rawName] = prefixed; } return rawName; } const xlinkNS = "http://www.w3.org/1999/xlink"; function patchAttr(el, key, value, isSVG, instance, isBoolean = isSpecialBooleanAttr(key)) { if (isSVG && key.startsWith("xlink:")) if (value == null) el.removeAttributeNS(xlinkNS, key.slice(6, key.length)); else el.setAttributeNS(xlinkNS, key, value); else if (value == null || isBoolean && !includeBooleanAttr(value)) el.removeAttribute(key); else el.setAttribute(key, isBoolean ? "" : isSymbol(value) ? String(value) : value); } function patchDOMProp(el, key, value, parentComponent, attrName) { if (key === "innerHTML" || key === "textContent") { if (value != null) el[key] = key === "innerHTML" ? unsafeToTrustedHTML(value) : value; return; } const tag = el.tagName; if (key === "value" && tag !== "PROGRESS" && !tag.includes("-")) { const oldValue = tag === "OPTION" ? el.getAttribute("value") || "" : el.value; const newValue = value == null ? el.type === "checkbox" ? "on" : "" : String(value); if (oldValue !== newValue || !("_value" in el)) el.value = newValue; if (value == null) el.removeAttribute(key); el._value = value; return; } let needRemove = false; if (value === "" || value == null) { const type = typeof el[key]; if (type === "boolean") value = includeBooleanAttr(value); else if (value == null && type === "string") { value = ""; needRemove = true; } else if (type === "number") { value = 0; needRemove = true; } } try { el[key] = value; } catch (e) {} needRemove && el.removeAttribute(attrName || key); } function addEventListener(el, event, handler, options) { el.addEventListener(event, handler, options); } function removeEventListener(el, event, handler, options) { el.removeEventListener(event, handler, options); } const veiKey = Symbol("_vei"); function patchEvent(el, rawName, prevValue, nextValue, instance = null) { const invokers = el[veiKey] || (el[veiKey] = {}); const existingInvoker = invokers[rawName]; if (nextValue && existingInvoker) existingInvoker.value = nextValue; else { const [name, options] = parseName(rawName); if (nextValue) { const invoker = invokers[rawName] = createInvoker(nextValue, instance); addEventListener(el, name, invoker, options); } else if (existingInvoker) { removeEventListener(el, name, existingInvoker, options); invokers[rawName] = void 0; } } } const optionsModifierRE = /(?:Once|Passive|Capture)$/; function parseName(name) { let options; if (optionsModifierRE.test(name)) { options = {}; let m; while (m = name.match(optionsModifierRE)) { name = name.slice(0, name.length - m[0].length); options[m[0].toLowerCase()] = true; } } const event = name[2] === ":" ? name.slice(3) : hyphenate(name.slice(2)); return [event, options]; } let cachedNow = 0; const p = /* @__PURE__ */ Promise.resolve(); const getNow = () => cachedNow || (p.then(() => cachedNow = 0), cachedNow = Date.now()); function createInvoker(initialValue, instance) { const invoker = (e) => { if (!e._vts) e._vts = Date.now(); else if (e._vts <= invoker.attached) return; callWithAsyncErrorHandling(patchStopImmediatePropagation(e, invoker.value), instance, 5, [e]); }; invoker.value = initialValue; invoker.attached = getNow(); return invoker; } function patchStopImmediatePropagation(e, value) { if (isArray(value)) { const originalStop = e.stopImmediatePropagation; e.stopImmediatePropagation = () => { originalStop.call(e); e._stopped = true; }; return value.map((fn) => (e2) => !e2._stopped && fn && fn(e2)); } else return value; } const isNativeOn = (key) => key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && key.charCodeAt(2) > 96 && key.charCodeAt(2) < 123; const patchProp = (el, key, prevValue, nextValue, namespace, parentComponent) => { const isSVG = namespace === "svg"; if (key === "class") patchClass(el, nextValue, isSVG); else if (key === "style") patchStyle(el, prevValue, nextValue); else if (isOn(key)) { if (!isModelListener(key)) patchEvent(el, key, prevValue, nextValue, parentComponent); } else if (key[0] === "." ? (key = key.slice(1), true) : key[0] === "^" ? (key = key.slice(1), false) : shouldSetAsProp(el, key, nextValue, isSVG)) { patchDOMProp(el, key, nextValue); if (!el.tagName.includes("-") && (key === "value" || key === "checked" || key === "selected")) patchAttr(el, key, nextValue, isSVG, parentComponent, key !== "value"); } else if (el._isVueCE && (/[A-Z]/.test(key) || !isString(nextValue))) patchDOMProp(el, camelize(key), nextValue, parentComponent, key); else { if (key === "true-value") el._trueValue = nextValue; else if (key === "false-value") el._falseValue = nextValue; patchAttr(el, key, nextValue, isSVG); } }; function shouldSetAsProp(el, key, value, isSVG) { if (isSVG) { if (key === "innerHTML" || key === "textContent") return true; if (key in el && isNativeOn(key) && isFunction(value)) return true; return false; } if (key === "spellcheck" || key === "draggable" || key === "translate" || key === "autocorrect") return false; if (key === "form") return false; if (key === "list" && el.tagName === "INPUT") return false; if (key === "type" && el.tagName === "TEXTAREA") return false; if (key === "width" || key === "height") { const tag = el.tagName; if (tag === "IMG" || tag === "VIDEO" || tag === "CANVAS" || tag === "SOURCE") return false; } if (isNativeOn(key) && isString(value)) return false; return key in el; } const moveCbKey = Symbol("_moveCb"); const enterCbKey = Symbol("_enterCb"); const getModelAssigner = (vnode) => { const fn = vnode.props["onUpdate:modelValue"] || false; return isArray(fn) ? (value) => invokeArrayFns(fn, value) : fn; }; function onCompositionStart(e) { e.target.composing = true; } function onCompositionEnd(e) { const target = e.target; if (target.composing) { target.composing = false; target.dispatchEvent(new Event("input")); } } const assignKey = Symbol("_assign"); const vModelText = { created(el, { modifiers: { lazy, trim, number } }, vnode) { el[assignKey] = getModelAssigner(vnode); const castToNumber = number || vnode.props && vnode.props.type === "number"; addEventListener(el, lazy ? "change" : "input", (e) => { if (e.target.composing) return; let domValue = el.value; if (trim) domValue = domValue.trim(); if (castToNumber) domValue = looseToNumber(domValue); el[assignKey](domValue); }); if (trim) addEventListener(el, "change", () => { el.value = el.value.trim(); }); if (!lazy) { addEventListener(el, "compositionstart", onCompositionStart); addEventListener(el, "compositionend", onCompositionEnd); addEventListener(el, "change", onCompositionEnd); } }, mounted(el, { value }) { el.value = value == null ? "" : value; }, beforeUpdate(el, { value, oldValue, modifiers: { lazy, trim, number } }, vnode) { el[assignKey] = getModelAssigner(vnode); if (el.composing) return; const elValue = (number || el.type === "number") && !/^0\d/.test(el.value) ? looseToNumber(el.value) : el.value; const newValue = value == null ? "" : value; if (elValue === newValue) return; if (document.activeElement === el && el.type !== "range") { if (lazy && value === oldValue) return; if (trim && el.value.trim() === newValue) return; } el.value = newValue; } }; const vModelCheckbox = { deep: true, created(el, _, vnode) { el[assignKey] = getModelAssigner(vnode); addEventListener(el, "change", () => { const modelValue = el._modelValue; const elementValue = getValue(el); const checked = el.checked; const assign$1 = el[assignKey]; if (isArray(modelValue)) { const index = looseIndexOf(modelValue, elementValue); const found = index !== -1; if (checked && !found) assign$1(modelValue.concat(elementValue)); else if (!checked && found) { const filtered = [...modelValue]; filtered.splice(index, 1); assign$1(filtered); } } else if (isSet(modelValue)) { const cloned = new Set(modelValue); if (checked) cloned.add(elementValue); else cloned.delete(elementValue); assign$1(cloned); } else assign$1(getCheckboxValue(el, checked)); }); }, mounted: setChecked, beforeUpdate(el, binding, vnode) { el[assignKey] = getModelAssigner(vnode); setChecked(el, binding, vnode); } }; function setChecked(el, { value, oldValue }, vnode) { el._modelValue = value; let checked; if (isArray(value)) checked = looseIndexOf(value, vnode.props.value) > -1; else if (isSet(value)) checked = value.has(vnode.props.value); else { if (value === oldValue) return; checked = looseEqual(value, getCheckboxValue(el, true)); } if (el.checked !== checked) el.checked = checked; } function getValue(el) { return "_value" in el ? el._value : el.value; } function getCheckboxValue(el, checked) { const key = checked ? "_trueValue" : "_falseValue"; return key in el ? el[key] : checked; } const keyNames = { esc: "escape", space: " ", up: "arrow-up", left: "arrow-left", right: "arrow-right", down: "arrow-down", delete: "backspace" }; const withKeys = (fn, modifiers) => { const cache = fn._withKeys || (fn._withKeys = {}); const cacheKey = modifiers.join("."); return cache[cacheKey] || (cache[cacheKey] = (event) => { if (!("key" in event)) return; const eventKey = hyphenate(event.key); if (modifiers.some((k) => k === eventKey || keyNames[k] === eventKey)) return fn(event); }); }; const rendererOptions = /* @__PURE__ */ extend({ patchProp }, nodeOps); let renderer; function ensureRenderer() { return renderer || (renderer = createRenderer(rendererOptions)); } const createApp = (...args) => { const app = ensureRenderer().createApp(...args); const { mount } = app; app.mount = (containerOrSelector) => { const container = normalizeContainer(containerOrSelector); if (!container) return; const component = app._component; if (!isFunction(component) && !component.render && !component.template) component.template = container.innerHTML; if (container.nodeType === 1) container.textContent = ""; const proxy = mount(container, false, resolveRootNamespace(container)); if (container instanceof Element) { container.removeAttribute("v-cloak"); container.setAttribute("data-v-app", ""); } return proxy; }; return app; }; function resolveRootNamespace(container) { if (container instanceof SVGElement) return "svg"; if (typeof MathMLElement === "function" && container instanceof MathMLElement) return "mathml"; } function normalizeContainer(container) { if (isString(container)) { const res = document.querySelector(container); return res; } return container; } const isBrowser = typeof document !== "undefined"; /** * Allows differentiating lazy components from functional components and vue-class-component * @internal * * @param component */ function isRouteComponent(component) { return typeof component === "object" || "displayName" in component || "props" in component || "__vccOpts" in component; } function isESModule(obj) { return obj.__esModule || obj[Symbol.toStringTag] === "Module" || obj.default && isRouteComponent(obj.default); } const assign = Object.assign; function applyToParams(fn, params) { const newParams = {}; for (const key in params) { const value = params[key]; newParams[key] = isArray$1(value) ? value.map(fn) : fn(value); } return newParams; } const noop = () => {}; /** * Typesafe alternative to Array.isArray * https://github.com/microsoft/TypeScript/pull/48228 */ const isArray$1 = Array.isArray; /** * Encoding Rules (␣ = Space) * - Path: ␣ " < > # ? { } * - Query: ␣ " < > # & = * - Hash: ␣ " < > ` * * On top of that, the RFC3986 (https://tools.ietf.org/html/rfc3986#section-2.2) * defines some extra characters to be encoded. Most browsers do not encode them * in encodeURI https://github.com/whatwg/url/issues/369, so it may be safer to * also encode `!'()*`. Leaving un-encoded only ASCII alphanumeric(`a-zA-Z0-9`) * plus `-._~`. This extra safety should be applied to query by patching the * string returned by encodeURIComponent encodeURI also encodes `[\]^`. `\` * should be encoded to avoid ambiguity. Browsers (IE, FF, C) transform a `\` * into a `/` if directly typed in. The _backtick_ (`````) should also be * encoded everywhere because some browsers like FF encode it when directly * written while others don't. Safari and IE don't encode ``"<>{}``` in hash. */ const HASH_RE = /#/g; const AMPERSAND_RE = /&/g; const SLASH_RE = /\//g; const EQUAL_RE = /=/g; const IM_RE = /\?/g; const PLUS_RE = /\+/g; /** * NOTE: It's not clear to me if we should encode the + symbol in queries, it * seems to be less flexible than not doing so and I can't find out the legacy * systems requiring this for regular requests like text/html. In the standard, * the encoding of the plus character is only mentioned for * application/x-www-form-urlencoded * (https://url.spec.whatwg.org/#urlencoded-parsing) and most browsers seems lo * leave the plus character as is in queries. To be more flexible, we allow the * plus character on the query, but it can also be manually encoded by the user. * * Resources: * - https://url.spec.whatwg.org/#urlencoded-parsing * - https://stackoverflow.com/questions/1634271/url-encoding-the-space-character-or-20 */ const ENC_BRACKET_OPEN_RE = /%5B/g; const ENC_BRACKET_CLOSE_RE = /%5D/g; const ENC_CARET_RE = /%5E/g; const ENC_BACKTICK_RE = /%60/g; const ENC_CURLY_OPEN_RE = /%7B/g; const ENC_PIPE_RE = /%7C/g; const ENC_CURLY_CLOSE_RE = /%7D/g; const ENC_SPACE_RE = /%20/g; /** * Encode characters that need to be encoded on the path, search and hash * sections of the URL. * * @internal * @param text - string to encode * @returns encoded string */ function commonEncode(text) { return encodeURI("" + text).replace(ENC_PIPE_RE, "|").replace(ENC_BRACKET_OPEN_RE, "[").replace(ENC_BRACKET_CLOSE_RE, "]"); } /** * Encode characters that need to be encoded on the hash section of the URL. * * @param text - string to encode * @returns encoded string */ function encodeHash(text) { return commonEncode(text).replace(ENC_CURLY_OPEN_RE, "{").replace(ENC_CURLY_CLOSE_RE, "}").replace(ENC_CARET_RE, "^"); } /** * Encode characters that need to be encoded query values on the query * section of the URL. * * @param text - string to encode * @returns encoded string */ function encodeQueryValue(text) { return commonEncode(text).replace(PLUS_RE, "%2B").replace(ENC_SPACE_RE, "+").replace(HASH_RE, "%23").replace(AMPERSAND_RE, "%26").replace(ENC_BACKTICK_RE, "`").replace(ENC_CURLY_OPEN_RE, "{").replace(ENC_CURLY_CLOSE_RE, "}").replace(ENC_CARET_RE, "^"); } /** * Like `encodeQueryValue` but also encodes the `=` character. * * @param text - string to encode */ function encodeQueryKey(text) { return encodeQueryValue(text).replace(EQUAL_RE, "%3D"); } /** * Encode characters that need to be encoded on the path section of the URL. * * @param text - string to encode * @returns encoded string */ function encodePath(text) { return commonEncode(text).replace(HASH_RE, "%23").replace(IM_RE, "%3F"); } /** * Encode characters that need to be encoded on the path section of the URL as a * param. This function encodes everything {@link encodePath} does plus the * slash (`/`) character. If `text` is `null` or `undefined`, returns an empty * string instead. * * @param text - string to encode * @returns encoded string */ function encodeParam(text) { return text == null ? "" : encodePath(text).replace(SLASH_RE, "%2F"); } /** * Decode text using `decodeURIComponent`. Returns the original text if it * fails. * * @param text - string to decode * @returns decoded string */ function decode(text) { try { return decodeURIComponent("" + text); } catch (err) {} return "" + text; } const TRAILING_SLASH_RE = /\/$/; const removeTrailingSlash = (path) => path.replace(TRAILING_SLASH_RE, ""); /** * Transforms a URI into a normalized history location * * @param parseQuery * @param location - URI to normalize * @param currentLocation - current absolute location. Allows resolving relative * paths. Must start with `/`. Defaults to `/` * @returns a normalized history location */ function parseURL(parseQuery$1, location$1, currentLocation = "/") { let path, query = {}, searchString = "", hash = ""; const hashPos = location$1.indexOf("#"); let searchPos = location$1.indexOf("?"); if (hashPos < searchPos && hashPos >= 0) searchPos = -1; if (searchPos > -1) { path = location$1.slice(0, searchPos); searchString = location$1.slice(searchPos + 1, hashPos > -1 ? hashPos : location$1.length); query = parseQuery$1(searchString); } if (hashPos > -1) { path = path || location$1.slice(0, hashPos); hash = location$1.slice(hashPos, location$1.length); } path = resolveRelativePath(path != null ? path : location$1, currentLocation); return { fullPath: path + (searchString && "?") + searchString + hash, path, query, hash: decode(hash) }; } /** * Stringifies a URL object * * @param stringifyQuery * @param location */ function stringifyURL(stringifyQuery$1, location$1) { const query = location$1.query ? stringifyQuery$1(location$1.query) : ""; return location$1.path + (query && "?") + query + (location$1.hash || ""); } /** * Strips off the base from the beginning of a location.pathname in a non-case-sensitive way. * * @param pathname - location.pathname * @param base - base to strip off */ function stripBase(pathname, base) { if (!base || !pathname.toLowerCase().startsWith(base.toLowerCase())) return pathname; return pathname.slice(base.length) || "/"; } /** * Checks if two RouteLocation are equal. This means that both locations are * pointing towards the same {@link RouteRecord} and that all `params`, `query` * parameters and `hash` are the same * * @param stringifyQuery - A function that takes a query object of type LocationQueryRaw and returns a string representation of it. * @param a - first {@link RouteLocation} * @param b - second {@link RouteLocation} */ function isSameRouteLocation(stringifyQuery$1, a, b) { const aLastIndex = a.matched.length - 1; const bLastIndex = b.matched.length - 1; return aLastIndex > -1 && aLastIndex === bLastIndex && isSameRouteRecord(a.matched[aLastIndex], b.matched[bLastIndex]) && isSameRouteLocationParams(a.params, b.params) && stringifyQuery$1(a.query) === stringifyQuery$1(b.query) && a.hash === b.hash; } /** * Check if two `RouteRecords` are equal. Takes into account aliases: they are * considered equal to the `RouteRecord` they are aliasing. * * @param a - first {@link RouteRecord} * @param b - second {@link RouteRecord} */ function isSameRouteRecord(a, b) { return (a.aliasOf || a) === (b.aliasOf || b); } function isSameRouteLocationParams(a, b) { if (Object.keys(a).length !== Object.keys(b).length) return false; for (const key in a) if (!isSameRouteLocationParamsValue(a[key], b[key])) return false; return true; } function isSameRouteLocationParamsValue(a, b) { return isArray$1(a) ? isEquivalentArray(a, b) : isArray$1(b) ? isEquivalentArray(b, a) : a === b; } /** * Check if two arrays are the same or if an array with one single entry is the * same as another primitive value. Used to check query and parameters * * @param a - array of values * @param b - array of values or a single value */ function isEquivalentArray(a, b) { return isArray$1(b) ? a.length === b.length && a.every((value, i) => value === b[i]) : a.length === 1 && a[0] === b; } /** * Resolves a relative path that starts with `.`. * * @param to - path location we are resolving * @param from - currentLocation.path, should start with `/` */ function resolveRelativePath(to, from) { if (to.startsWith("/")) return to; if (!to) return from; const fromSegments = from.split("/"); const toSegments = to.split("/"); const lastToSegment = toSegments[toSegments.length - 1]; if (lastToSegment === ".." || lastToSegment === ".") toSegments.push(""); let position = fromSegments.length - 1; let toPosition; let segment; for (toPosition = 0; toPosition < toSegments.length; toPosition++) { segment = toSegments[toPosition]; if (segment === ".") continue; if (segment === "..") { if (position > 1) position--; } else break; } return fromSegments.slice(0, position).join("/") + "/" + toSegments.slice(toPosition).join("/"); } /** * Initial route location where the router is. Can be used in navigation guards * to differentiate the initial navigation. * * @example * ```js * import { START_LOCATION } from 'vue-router' * * router.beforeEach((to, from) => { * if (from === START_LOCATION) { * // initial navigation * } * }) * ``` */ const START_LOCATION_NORMALIZED = { path: "/", name: void 0, params: {}, query: {}, hash: "", fullPath: "/", matched: [], meta: {}, redirectedFrom: void 0 }; var NavigationType; (function(NavigationType$1) { NavigationType$1["pop"] = "pop"; NavigationType$1["push"] = "push"; })(NavigationType || (NavigationType = {})); var NavigationDirection; (function(NavigationDirection$1) { NavigationDirection$1["back"] = "back"; NavigationDirection$1["forward"] = "forward"; NavigationDirection$1["unknown"] = ""; })(NavigationDirection || (NavigationDirection = {})); /** * Normalizes a base by removing any trailing slash and reading the base tag if * present. * * @param base - base to normalize */ function normalizeBase(base) { if (!base) if (isBrowser) { const baseEl = document.querySelector("base"); base = baseEl && baseEl.getAttribute("href") || "/"; base = base.replace(/^\w+:\/\/[^\/]+/, ""); } else base = "/"; if (base[0] !== "/" && base[0] !== "#") base = "/" + base; return removeTrailingSlash(base); } const BEFORE_HASH_RE = /^[^#]+#/; function createHref(base, location$1) { return base.replace(BEFORE_HASH_RE, "#") + location$1; } function getElementPosition(el, offset) { const docRect = document.documentElement.getBoundingClientRect(); const elRect = el.getBoundingClientRect(); return { behavior: offset.behavior, left: elRect.left - docRect.left - (offset.left || 0), top: elRect.top - docRect.top - (offset.top || 0) }; } const computeScrollPosition = () => ({ left: window.scrollX, top: window.scrollY }); function scrollToPosition(position) { let scrollToOptions; if ("el" in position) { const positionEl = position.el; const isIdSelector = typeof positionEl === "string" && positionEl.startsWith("#"); const el = typeof positionEl === "string" ? isIdSelector ? document.getElementById(positionEl.slice(1)) : document.querySelector(positionEl) : positionEl; if (!el) return; scrollToOptions = getElementPosition(el, position); } else scrollToOptions = position; if ("scrollBehavior" in document.documentElement.style) window.scrollTo(scrollToOptions); else window.scrollTo(scrollToOptions.left != null ? scrollToOptions.left : window.scrollX, scrollToOptions.top != null ? scrollToOptions.top : window.scrollY); } function getScrollKey(path, delta) { const position = history.state ? history.state.position - delta : -1; return position + path; } const scrollPositions = new Map(); function saveScrollPosition(key, scrollPosition) { scrollPositions.set(key, scrollPosition); } function getSavedScrollPosition(key) { const scroll = scrollPositions.get(key); scrollPositions.delete(key); return scroll; } /** * ScrollBehavior instance used by the router to compute and restore the scroll * position when navigating. */ let createBaseLocation = () => location.protocol + "//" + location.host; /** * Creates a normalized history location from a window.location object * @param base - The base path * @param location - The window.location object */ function createCurrentLocation(base, location$1) { const { pathname, search, hash } = location$1; const hashPos = base.indexOf("#"); if (hashPos > -1) { let slicePos = hash.includes(base.slice(hashPos)) ? base.slice(hashPos).length : 1; let pathFromHash = hash.slice(slicePos); if (pathFromHash[0] !== "/") pathFromHash = "/" + pathFromHash; return stripBase(pathFromHash, ""); } const path = stripBase(pathname, base); return path + search + hash; } function useHistoryListeners(base, historyState, currentLocation, replace) { let listeners = []; let teardowns = []; let pauseState = null; const popStateHandler = ({ state }) => { const to = createCurrentLocation(base, location); const from = currentLocation.value; const fromState = historyState.value; let delta = 0; if (state) { currentLocation.value = to; historyState.value = state; if (pauseState && pauseState === from) { pauseState = null; return; } delta = fromState ? state.position - fromState.position : 0; } else replace(to); listeners.forEach((listener) => { listener(currentLocation.value, from, { delta, type: NavigationType.pop, direction: delta ? delta > 0 ? NavigationDirection.forward : NavigationDirection.back : NavigationDirection.unknown }); }); }; function pauseListeners() { pauseState = currentLocation.value; } function listen(callback) { listeners.push(callback); const teardown = () => { const index = listeners.indexOf(callback); if (index > -1) listeners.splice(index, 1); }; teardowns.push(teardown); return teardown; } function beforeUnloadListener() { const { history: history$1 } = window; if (!history$1.state) return; history$1.replaceState(assign({}, history$1.state, { scroll: computeScrollPosition() }), ""); } function destroy() { for (const teardown of teardowns) teardown(); teardowns = []; window.removeEventListener("popstate", popStateHandler); window.removeEventListener("beforeunload", beforeUnloadListener); } window.addEventListener("popstate", popStateHandler); window.addEventListener("beforeunload", beforeUnloadListener, { passive: true }); return { pauseListeners, listen, destroy }; } /** * Creates a state object */ function buildState(back, current, forward, replaced = false, computeScroll = false) { return { back, current, forward, replaced, position: window.history.length, scroll: computeScroll ? computeScrollPosition() : null }; } function useHistoryStateNavigation(base) { const { history: history$1, location: location$1 } = window; const currentLocation = { value: createCurrentLocation(base, location$1) }; const historyState = { value: history$1.state }; if (!historyState.value) changeLocation(currentLocation.value, { back: null, current: currentLocation.value, forward: null, position: history$1.length - 1, replaced: true, scroll: null }, true); function changeLocation(to, state, replace$1) { /** * if a base tag is provided, and we are on a normal domain, we have to * respect the provided `base` attribute because pushState() will use it and * potentially erase anything before the `#` like at * https://github.com/vuejs/router/issues/685 where a base of * `/folder/#` but a base of `/` would erase the `/folder/` section. If * there is no host, the `` tag makes no sense and if there isn't a * base tag we can just use everything after the `#`. */ const hashIndex = base.indexOf("#"); const url = hashIndex > -1 ? (location$1.host && document.querySelector("base") ? base : base.slice(hashIndex)) + to : createBaseLocation() + base + to; try { history$1[replace$1 ? "replaceState" : "pushState"](state, "", url); historyState.value = state; } catch (err) { console.error(err); location$1[replace$1 ? "replace" : "assign"](url); } } function replace(to, data) { const state = assign({}, history$1.state, buildState( historyState.value.back, // keep back and forward entries but override current position to, historyState.value.forward, true ), data, { position: historyState.value.position }); changeLocation(to, state, true); currentLocation.value = to; } function push(to, data) { const currentState = assign( {}, // use current history state to gracefully handle a wrong call to // history.replaceState // https://github.com/vuejs/router/issues/366 historyState.value, history$1.state, { forward: to, scroll: computeScrollPosition() } ); changeLocation(currentState.current, currentState, true); const state = assign({}, buildState(currentLocation.value, to, null), { position: currentState.position + 1 }, data); changeLocation(to, state, false); currentLocation.value = to; } return { location: currentLocation, state: historyState, push, replace }; } /** * Creates an HTML5 history. Most common history for single page applications. * * @param base - */ function createWebHistory(base) { base = normalizeBase(base); const historyNavigation = useHistoryStateNavigation(base); const historyListeners = useHistoryListeners(base, historyNavigation.state, historyNavigation.location, historyNavigation.replace); function go(delta, triggerListeners = true) { if (!triggerListeners) historyListeners.pauseListeners(); history.go(delta); } const routerHistory = assign({ location: "", base, go, createHref: createHref.bind(null, base) }, historyNavigation, historyListeners); Object.defineProperty(routerHistory, "location", { enumerable: true, get: () => historyNavigation.location.value }); Object.defineProperty(routerHistory, "state", { enumerable: true, get: () => historyNavigation.state.value }); return routerHistory; } /** * Creates a hash history. Useful for web applications with no host (e.g. `file://`) or when configuring a server to * handle any URL is not possible. * * @param base - optional base to provide. Defaults to `location.pathname + location.search` If there is a `` tag * in the `head`, its value will be ignored in favor of this parameter **but note it affects all the history.pushState() * calls**, meaning that if you use a `` tag, it's `href` value **has to match this parameter** (ignoring anything * after the `#`). * * @example * ```js * // at https://example.com/folder * createWebHashHistory() // gives a url of `https://example.com/folder#` * createWebHashHistory('/folder/') // gives a url of `https://example.com/folder/#` * // if the `#` is provided in the base, it won't be added by `createWebHashHistory` * createWebHashHistory('/folder/#/app/') // gives a url of `https://example.com/folder/#/app/` * // you should avoid doing this because it changes the original url and breaks copying urls * createWebHashHistory('/other-folder/') // gives a url of `https://example.com/other-folder/#` * * // at file:///usr/etc/folder/index.html * // for locations with no `host`, the base is ignored * createWebHashHistory('/iAmIgnored') // gives a url of `file:///usr/etc/folder/index.html#` * ``` */ function createWebHashHistory(base) { base = location.host ? base || location.pathname + location.search : ""; if (!base.includes("#")) base += "#"; return createWebHistory(base); } function isRouteLocation(route) { return typeof route === "string" || route && typeof route === "object"; } function isRouteName(name) { return typeof name === "string" || typeof name === "symbol"; } const NavigationFailureSymbol = Symbol(""); /** * Enumeration with all possible types for navigation failures. Can be passed to * {@link isNavigationFailure} to check for specific failures. */ var NavigationFailureType; (function(NavigationFailureType$1) { /** * An aborted navigation is a navigation that failed because a navigation * guard returned `false` or called `next(false)` */ NavigationFailureType$1[NavigationFailureType$1["aborted"] = 4] = "aborted"; /** * A cancelled navigation is a navigation that failed because a more recent * navigation finished started (not necessarily finished). */ NavigationFailureType$1[NavigationFailureType$1["cancelled"] = 8] = "cancelled"; /** * A duplicated navigation is a navigation that failed because it was * initiated while already being at the exact same location. */ NavigationFailureType$1[NavigationFailureType$1["duplicated"] = 16] = "duplicated"; })(NavigationFailureType || (NavigationFailureType = {})); /** * Creates a typed NavigationFailure object. * @internal * @param type - NavigationFailureType * @param params - { from, to } */ function createRouterError(type, params) { return assign(new Error(), { type, [NavigationFailureSymbol]: true }, params); } function isNavigationFailure(error, type) { return error instanceof Error && NavigationFailureSymbol in error && (type == null || !!(error.type & type)); } const BASE_PARAM_PATTERN = "[^/]+?"; const BASE_PATH_PARSER_OPTIONS = { sensitive: false, strict: false, start: true, end: true }; const REGEX_CHARS_RE = /[.+*?^${}()[\]/\\]/g; /** * Creates a path parser from an array of Segments (a segment is an array of Tokens) * * @param segments - array of segments returned by tokenizePath * @param extraOptions - optional options for the regexp * @returns a PathParser */ function tokensToParser(segments, extraOptions) { const options = assign({}, BASE_PATH_PARSER_OPTIONS, extraOptions); const score = []; let pattern = options.start ? "^" : ""; const keys = []; for (const segment of segments) { const segmentScores = segment.length ? [] : [90]; if (options.strict && !segment.length) pattern += "/"; for (let tokenIndex = 0; tokenIndex < segment.length; tokenIndex++) { const token = segment[tokenIndex]; let subSegmentScore = 40 + (options.sensitive ? .25 : 0); if (token.type === 0) { if (!tokenIndex) pattern += "/"; pattern += token.value.replace(REGEX_CHARS_RE, "\\$&"); subSegmentScore += 40; } else if (token.type === 1) { const { value, repeatable, optional, regexp } = token; keys.push({ name: value, repeatable, optional }); const re$1 = regexp ? regexp : BASE_PARAM_PATTERN; if (re$1 !== BASE_PARAM_PATTERN) { subSegmentScore += 10; try { new RegExp(`(${re$1})`); } catch (err) { throw new Error(`Invalid custom RegExp for param "${value}" (${re$1}): ` + err.message); } } let subPattern = repeatable ? `((?:${re$1})(?:/(?:${re$1}))*)` : `(${re$1})`; if (!tokenIndex) subPattern = optional && segment.length < 2 ? `(?:/${subPattern})` : "/" + subPattern; if (optional) subPattern += "?"; pattern += subPattern; subSegmentScore += 20; if (optional) subSegmentScore += -8; if (repeatable) subSegmentScore += -20; if (re$1 === ".*") subSegmentScore += -50; } segmentScores.push(subSegmentScore); } score.push(segmentScores); } if (options.strict && options.end) { const i = score.length - 1; score[i][score[i].length - 1] += .7000000000000001; } if (!options.strict) pattern += "/?"; if (options.end) pattern += "$"; else if (options.strict && !pattern.endsWith("/")) pattern += "(?:/|$)"; const re = new RegExp(pattern, options.sensitive ? "" : "i"); function parse(path) { const match = path.match(re); const params = {}; if (!match) return null; for (let i = 1; i < match.length; i++) { const value = match[i] || ""; const key = keys[i - 1]; params[key.name] = value && key.repeatable ? value.split("/") : value; } return params; } function stringify(params) { let path = ""; let avoidDuplicatedSlash = false; for (const segment of segments) { if (!avoidDuplicatedSlash || !path.endsWith("/")) path += "/"; avoidDuplicatedSlash = false; for (const token of segment) if (token.type === 0) path += token.value; else if (token.type === 1) { const { value, repeatable, optional } = token; const param = value in params ? params[value] : ""; if (isArray$1(param) && !repeatable) throw new Error(`Provided param "${value}" is an array but it is not repeatable (* or + modifiers)`); const text = isArray$1(param) ? param.join("/") : param; if (!text) if (optional) { if (segment.length < 2) if (path.endsWith("/")) path = path.slice(0, -1); else avoidDuplicatedSlash = true; } else throw new Error(`Missing required param "${value}"`); path += text; } } return path || "/"; } return { re, score, keys, parse, stringify }; } /** * Compares an array of numbers as used in PathParser.score and returns a * number. This function can be used to `sort` an array * * @param a - first array of numbers * @param b - second array of numbers * @returns 0 if both are equal, < 0 if a should be sorted first, > 0 if b * should be sorted first */ function compareScoreArray(a, b) { let i = 0; while (i < a.length && i < b.length) { const diff = b[i] - a[i]; if (diff) return diff; i++; } if (a.length < b.length) return a.length === 1 && a[0] === 80 ? -1 : 1; else if (a.length > b.length) return b.length === 1 && b[0] === 80 ? 1 : -1; return 0; } /** * Compare function that can be used with `sort` to sort an array of PathParser * * @param a - first PathParser * @param b - second PathParser * @returns 0 if both are equal, < 0 if a should be sorted first, > 0 if b */ function comparePathParserScore(a, b) { let i = 0; const aScore = a.score; const bScore = b.score; while (i < aScore.length && i < bScore.length) { const comp = compareScoreArray(aScore[i], bScore[i]); if (comp) return comp; i++; } if (Math.abs(bScore.length - aScore.length) === 1) { if (isLastScoreNegative(aScore)) return 1; if (isLastScoreNegative(bScore)) return -1; } return bScore.length - aScore.length; } /** * This allows detecting splats at the end of a path: /home/:id(.*)* * * @param score - score to check * @returns true if the last entry is negative */ function isLastScoreNegative(score) { const last = score[score.length - 1]; return score.length > 0 && last[last.length - 1] < 0; } const ROOT_TOKEN = { type: 0, value: "" }; const VALID_PARAM_RE = /[a-zA-Z0-9_]/; function tokenizePath(path) { if (!path) return [[]]; if (path === "/") return [[ROOT_TOKEN]]; if (!path.startsWith("/")) throw new Error(`Invalid path "${path}"`); function crash(message) { throw new Error(`ERR (${state})/"${buffer}": ${message}`); } let state = 0; let previousState = state; const tokens = []; let segment; function finalizeSegment() { if (segment) tokens.push(segment); segment = []; } let i = 0; let char; let buffer = ""; let customRe = ""; function consumeBuffer() { if (!buffer) return; if (state === 0) segment.push({ type: 0, value: buffer }); else if (state === 1 || state === 2 || state === 3) { if (segment.length > 1 && (char === "*" || char === "+")) crash(`A repeatable param (${buffer}) must be alone in its segment. eg: '/:ids+.`); segment.push({ type: 1, value: buffer, regexp: customRe, repeatable: char === "*" || char === "+", optional: char === "*" || char === "?" }); } else crash("Invalid state to consume buffer"); buffer = ""; } function addCharToBuffer() { buffer += char; } while (i < path.length) { char = path[i++]; if (char === "\\" && state !== 2) { previousState = state; state = 4; continue; } switch (state) { case 0: if (char === "/") { if (buffer) consumeBuffer(); finalizeSegment(); } else if (char === ":") { consumeBuffer(); state = 1; } else addCharToBuffer(); break; case 4: addCharToBuffer(); state = previousState; break; case 1: if (char === "(") state = 2; else if (VALID_PARAM_RE.test(char)) addCharToBuffer(); else { consumeBuffer(); state = 0; if (char !== "*" && char !== "?" && char !== "+") i--; } break; case 2: if (char === ")") if (customRe[customRe.length - 1] == "\\") customRe = customRe.slice(0, -1) + char; else state = 3; else customRe += char; break; case 3: consumeBuffer(); state = 0; if (char !== "*" && char !== "?" && char !== "+") i--; customRe = ""; break; default: crash("Unknown state"); break; } } if (state === 2) crash(`Unfinished custom RegExp for param "${buffer}"`); consumeBuffer(); finalizeSegment(); return tokens; } function createRouteRecordMatcher(record, parent, options) { const parser = tokensToParser(tokenizePath(record.path), options); const matcher = assign(parser, { record, parent, children: [], alias: [] }); if (parent) { if (!matcher.record.aliasOf === !parent.record.aliasOf) parent.children.push(matcher); } return matcher; } /** * Creates a Router Matcher. * * @internal * @param routes - array of initial routes * @param globalOptions - global route options */ function createRouterMatcher(routes, globalOptions) { const matchers = []; const matcherMap = new Map(); globalOptions = mergeOptions({ strict: false, end: true, sensitive: false }, globalOptions); function getRecordMatcher(name) { return matcherMap.get(name); } function addRoute(record, parent, originalRecord) { const isRootAdd = !originalRecord; const mainNormalizedRecord = normalizeRouteRecord(record); mainNormalizedRecord.aliasOf = originalRecord && originalRecord.record; const options = mergeOptions(globalOptions, record); const normalizedRecords = [mainNormalizedRecord]; if ("alias" in record) { const aliases = typeof record.alias === "string" ? [record.alias] : record.alias; for (const alias of aliases) normalizedRecords.push( // we need to normalize again to ensure the `mods` property // being non enumerable normalizeRouteRecord(assign({}, mainNormalizedRecord, { components: originalRecord ? originalRecord.record.components : mainNormalizedRecord.components, path: alias, aliasOf: originalRecord ? originalRecord.record : mainNormalizedRecord })) ); } let matcher; let originalMatcher; for (const normalizedRecord of normalizedRecords) { const { path } = normalizedRecord; if (parent && path[0] !== "/") { const parentPath = parent.record.path; const connectingSlash = parentPath[parentPath.length - 1] === "/" ? "" : "/"; normalizedRecord.path = parent.record.path + (path && connectingSlash + path); } matcher = createRouteRecordMatcher(normalizedRecord, parent, options); if (originalRecord) originalRecord.alias.push(matcher); else { originalMatcher = originalMatcher || matcher; if (originalMatcher !== matcher) originalMatcher.alias.push(matcher); if (isRootAdd && record.name && !isAliasRecord(matcher)) removeRoute(record.name); } if (isMatchable(matcher)) insertMatcher(matcher); if (mainNormalizedRecord.children) { const children = mainNormalizedRecord.children; for (let i = 0; i < children.length; i++) addRoute(children[i], matcher, originalRecord && originalRecord.children[i]); } originalRecord = originalRecord || matcher; } return originalMatcher ? () => { removeRoute(originalMatcher); } : noop; } function removeRoute(matcherRef) { if (isRouteName(matcherRef)) { const matcher = matcherMap.get(matcherRef); if (matcher) { matcherMap.delete(matcherRef); matchers.splice(matchers.indexOf(matcher), 1); matcher.children.forEach(removeRoute); matcher.alias.forEach(removeRoute); } } else { const index = matchers.indexOf(matcherRef); if (index > -1) { matchers.splice(index, 1); if (matcherRef.record.name) matcherMap.delete(matcherRef.record.name); matcherRef.children.forEach(removeRoute); matcherRef.alias.forEach(removeRoute); } } } function getRoutes() { return matchers; } function insertMatcher(matcher) { const index = findInsertionIndex(matcher, matchers); matchers.splice(index, 0, matcher); if (matcher.record.name && !isAliasRecord(matcher)) matcherMap.set(matcher.record.name, matcher); } function resolve(location$1, currentLocation) { let matcher; let params = {}; let path; let name; if ("name" in location$1 && location$1.name) { matcher = matcherMap.get(location$1.name); if (!matcher) throw createRouterError(1, { location: location$1 }); name = matcher.record.name; params = assign( // paramsFromLocation is a new object paramsFromLocation( currentLocation.params, // only keep params that exist in the resolved location // only keep optional params coming from a parent record matcher.keys.filter((k) => !k.optional).concat(matcher.parent ? matcher.parent.keys.filter((k) => k.optional) : []).map((k) => k.name) ), // discard any existing params in the current location that do not exist here // #1497 this ensures better active/exact matching location$1.params && paramsFromLocation(location$1.params, matcher.keys.map((k) => k.name)) ); path = matcher.stringify(params); } else if (location$1.path != null) { path = location$1.path; matcher = matchers.find((m) => m.re.test(path)); if (matcher) { params = matcher.parse(path); name = matcher.record.name; } } else { matcher = currentLocation.name ? matcherMap.get(currentLocation.name) : matchers.find((m) => m.re.test(currentLocation.path)); if (!matcher) throw createRouterError(1, { location: location$1, currentLocation }); name = matcher.record.name; params = assign({}, currentLocation.params, location$1.params); path = matcher.stringify(params); } const matched = []; let parentMatcher = matcher; while (parentMatcher) { matched.unshift(parentMatcher.record); parentMatcher = parentMatcher.parent; } return { name, path, params, matched, meta: mergeMetaFields(matched) }; } routes.forEach((route) => addRoute(route)); function clearRoutes() { matchers.length = 0; matcherMap.clear(); } return { addRoute, resolve, removeRoute, clearRoutes, getRoutes, getRecordMatcher }; } function paramsFromLocation(params, keys) { const newParams = {}; for (const key of keys) if (key in params) newParams[key] = params[key]; return newParams; } /** * Normalizes a RouteRecordRaw. Creates a copy * * @param record * @returns the normalized version */ function normalizeRouteRecord(record) { const normalized = { path: record.path, redirect: record.redirect, name: record.name, meta: record.meta || {}, aliasOf: record.aliasOf, beforeEnter: record.beforeEnter, props: normalizeRecordProps(record), children: record.children || [], instances: {}, leaveGuards: new Set(), updateGuards: new Set(), enterCallbacks: {}, components: "components" in record ? record.components || null : record.component && { default: record.component } }; Object.defineProperty(normalized, "mods", { value: {} }); return normalized; } /** * Normalize the optional `props` in a record to always be an object similar to * components. Also accept a boolean for components. * @param record */ function normalizeRecordProps(record) { const propsObject = {}; const props = record.props || false; if ("component" in record) propsObject.default = props; else for (const name in record.components) propsObject[name] = typeof props === "object" ? props[name] : props; return propsObject; } /** * Checks if a record or any of its parent is an alias * @param record */ function isAliasRecord(record) { while (record) { if (record.record.aliasOf) return true; record = record.parent; } return false; } /** * Merge meta fields of an array of records * * @param matched - array of matched records */ function mergeMetaFields(matched) { return matched.reduce((meta, record) => assign(meta, record.meta), {}); } function mergeOptions(defaults, partialOptions) { const options = {}; for (const key in defaults) options[key] = key in partialOptions ? partialOptions[key] : defaults[key]; return options; } /** * Performs a binary search to find the correct insertion index for a new matcher. * * Matchers are primarily sorted by their score. If scores are tied then we also consider parent/child relationships, * with descendants coming before ancestors. If there's still a tie, new routes are inserted after existing routes. * * @param matcher - new matcher to be inserted * @param matchers - existing matchers */ function findInsertionIndex(matcher, matchers) { let lower = 0; let upper = matchers.length; while (lower !== upper) { const mid = lower + upper >> 1; const sortOrder = comparePathParserScore(matcher, matchers[mid]); if (sortOrder < 0) upper = mid; else lower = mid + 1; } const insertionAncestor = getInsertionAncestor(matcher); if (insertionAncestor) upper = matchers.lastIndexOf(insertionAncestor, upper - 1); return upper; } function getInsertionAncestor(matcher) { let ancestor = matcher; while (ancestor = ancestor.parent) if (isMatchable(ancestor) && comparePathParserScore(matcher, ancestor) === 0) return ancestor; return; } /** * Checks if a matcher can be reachable. This means if it's possible to reach it as a route. For example, routes without * a component, or name, or redirect, are just used to group other routes. * @param matcher * @param matcher.record record of the matcher * @returns */ function isMatchable({ record }) { return !!(record.name || record.components && Object.keys(record.components).length || record.redirect); } /** * Transforms a queryString into a {@link LocationQuery} object. Accept both, a * version with the leading `?` and without Should work as URLSearchParams * @internal * * @param search - search string to parse * @returns a query object */ function parseQuery(search) { const query = {}; if (search === "" || search === "?") return query; const hasLeadingIM = search[0] === "?"; const searchParams = (hasLeadingIM ? search.slice(1) : search).split("&"); for (let i = 0; i < searchParams.length; ++i) { const searchParam = searchParams[i].replace(PLUS_RE, " "); const eqPos = searchParam.indexOf("="); const key = decode(eqPos < 0 ? searchParam : searchParam.slice(0, eqPos)); const value = eqPos < 0 ? null : decode(searchParam.slice(eqPos + 1)); if (key in query) { let currentValue = query[key]; if (!isArray$1(currentValue)) currentValue = query[key] = [currentValue]; currentValue.push(value); } else query[key] = value; } return query; } /** * Stringifies a {@link LocationQueryRaw} object. Like `URLSearchParams`, it * doesn't prepend a `?` * * @internal * * @param query - query object to stringify * @returns string version of the query without the leading `?` */ function stringifyQuery(query) { let search = ""; for (let key in query) { const value = query[key]; key = encodeQueryKey(key); if (value == null) { if (value !== void 0) search += (search.length ? "&" : "") + key; continue; } const values = isArray$1(value) ? value.map((v) => v && encodeQueryValue(v)) : [value && encodeQueryValue(value)]; values.forEach((value$1) => { if (value$1 !== void 0) { search += (search.length ? "&" : "") + key; if (value$1 != null) search += "=" + value$1; } }); } return search; } /** * Transforms a {@link LocationQueryRaw} into a {@link LocationQuery} by casting * numbers into strings, removing keys with an undefined value and replacing * undefined with null in arrays * * @param query - query object to normalize * @returns a normalized query object */ function normalizeQuery(query) { const normalizedQuery = {}; for (const key in query) { const value = query[key]; if (value !== void 0) normalizedQuery[key] = isArray$1(value) ? value.map((v) => v == null ? null : "" + v) : value == null ? value : "" + value; } return normalizedQuery; } /** * RouteRecord being rendered by the closest ancestor Router View. Used for * `onBeforeRouteUpdate` and `onBeforeRouteLeave`. rvlm stands for Router View * Location Matched * * @internal */ const matchedRouteKey = Symbol(""); /** * Allows overriding the router view depth to control which component in * `matched` is rendered. rvd stands for Router View Depth * * @internal */ const viewDepthKey = Symbol(""); /** * Allows overriding the router instance returned by `useRouter` in tests. r * stands for router * * @internal */ const routerKey = Symbol(""); /** * Allows overriding the current route returned by `useRoute` in tests. rl * stands for route location * * @internal */ const routeLocationKey = Symbol(""); /** * Allows overriding the current route used by router-view. Internally this is * used when the `route` prop is passed. * * @internal */ const routerViewLocationKey = Symbol(""); /** * Create a list of callbacks that can be reset. Used to create before and after navigation guards list */ function useCallbacks() { let handlers = []; function add(handler) { handlers.push(handler); return () => { const i = handlers.indexOf(handler); if (i > -1) handlers.splice(i, 1); }; } function reset() { handlers = []; } return { add, list: () => handlers.slice(), reset }; } function guardToPromiseFn(guard, to, from, record, name, runWithContext = (fn) => fn()) { const enterCallbackArray = record && (record.enterCallbacks[name] = record.enterCallbacks[name] || []); return () => new Promise((resolve, reject) => { const next = (valid) => { if (valid === false) reject(createRouterError(4, { from, to })); else if (valid instanceof Error) reject(valid); else if (isRouteLocation(valid)) reject(createRouterError(2, { from: to, to: valid })); else { if (enterCallbackArray && record.enterCallbacks[name] === enterCallbackArray && typeof valid === "function") enterCallbackArray.push(valid); resolve(); } }; const guardReturn = runWithContext(() => guard.call(record && record.instances[name], to, from, next)); let guardCall = Promise.resolve(guardReturn); if (guard.length < 3) guardCall = guardCall.then(next); guardCall.catch((err) => reject(err)); }); } function extractComponentsGuards(matched, guardType, to, from, runWithContext = (fn) => fn()) { const guards = []; for (const record of matched) for (const name in record.components) { let rawComponent = record.components[name]; if (guardType !== "beforeRouteEnter" && !record.instances[name]) continue; if (isRouteComponent(rawComponent)) { const options = rawComponent.__vccOpts || rawComponent; const guard = options[guardType]; guard && guards.push(guardToPromiseFn(guard, to, from, record, name, runWithContext)); } else { let componentPromise = rawComponent(); guards.push(() => componentPromise.then((resolved) => { if (!resolved) throw new Error(`Couldn't resolve component "${name}" at "${record.path}"`); const resolvedComponent = isESModule(resolved) ? resolved.default : resolved; record.mods[name] = resolved; record.components[name] = resolvedComponent; const options = resolvedComponent.__vccOpts || resolvedComponent; const guard = options[guardType]; return guard && guardToPromiseFn(guard, to, from, record, name, runWithContext)(); })); } } return guards; } /** * Returns the internal behavior of a {@link RouterLink} without the rendering part. * * @param props - a `to` location and an optional `replace` flag */ function useLink(props) { const router = inject(routerKey); const currentRoute = inject(routeLocationKey); let hasPrevious = false; let previousTo = null; const route = computed(() => { const to = unref(props.to); return router.resolve(to); }); const activeRecordIndex = computed(() => { const { matched } = route.value; const { length } = matched; const routeMatched = matched[length - 1]; const currentMatched = currentRoute.matched; if (!routeMatched || !currentMatched.length) return -1; const index = currentMatched.findIndex(isSameRouteRecord.bind(null, routeMatched)); if (index > -1) return index; const parentRecordPath = getOriginalPath(matched[length - 2]); return length > 1 && getOriginalPath(routeMatched) === parentRecordPath && currentMatched[currentMatched.length - 1].path !== parentRecordPath ? currentMatched.findIndex(isSameRouteRecord.bind(null, matched[length - 2])) : index; }); const isActive = computed(() => activeRecordIndex.value > -1 && includesParams(currentRoute.params, route.value.params)); const isExactActive = computed(() => activeRecordIndex.value > -1 && activeRecordIndex.value === currentRoute.matched.length - 1 && isSameRouteLocationParams(currentRoute.params, route.value.params)); function navigate(e = {}) { if (guardEvent(e)) { const p$1 = router[unref(props.replace) ? "replace" : "push"]( unref(props.to) // avoid uncaught errors are they are logged anyway ).catch(noop); if (props.viewTransition && typeof document !== "undefined" && "startViewTransition" in document) document.startViewTransition(() => p$1); return p$1; } return Promise.resolve(); } /** * NOTE: update {@link _RouterLinkI}'s `$slots` type when updating this */ return { route, href: computed(() => route.value.href), isActive, isExactActive, navigate }; } function preferSingleVNode(vnodes) { return vnodes.length === 1 ? vnodes[0] : vnodes; } const RouterLinkImpl = /* @__PURE__ */ defineComponent({ name: "RouterLink", compatConfig: { MODE: 3 }, props: { to: { type: [String, Object], required: true }, replace: Boolean, activeClass: String, exactActiveClass: String, custom: Boolean, ariaCurrentValue: { type: String, default: "page" }, viewTransition: Boolean }, useLink, setup(props, { slots }) { const link = reactive(useLink(props)); const { options } = inject(routerKey); const elClass = computed(() => ({ [getLinkClass(props.activeClass, options.linkActiveClass, "router-link-active")]: link.isActive, [getLinkClass(props.exactActiveClass, options.linkExactActiveClass, "router-link-exact-active")]: link.isExactActive })); return () => { const children = slots.default && preferSingleVNode(slots.default(link)); return props.custom ? children : h("a", { "aria-current": link.isExactActive ? props.ariaCurrentValue : null, href: link.href, onClick: link.navigate, class: elClass.value }, children); }; } }); /** * Component to render a link that triggers a navigation on click. */ const RouterLink = RouterLinkImpl; function guardEvent(e) { if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return; if (e.defaultPrevented) return; if (e.button !== void 0 && e.button !== 0) return; if (e.currentTarget && e.currentTarget.getAttribute) { const target = e.currentTarget.getAttribute("target"); if (/\b_blank\b/i.test(target)) return; } if (e.preventDefault) e.preventDefault(); return true; } function includesParams(outer, inner) { for (const key in inner) { const innerValue = inner[key]; const outerValue = outer[key]; if (typeof innerValue === "string") { if (innerValue !== outerValue) return false; } else if (!isArray$1(outerValue) || outerValue.length !== innerValue.length || innerValue.some((value, i) => value !== outerValue[i])) return false; } return true; } /** * Get the original path value of a record by following its aliasOf * @param record */ function getOriginalPath(record) { return record ? record.aliasOf ? record.aliasOf.path : record.path : ""; } /** * Utility class to get the active class based on defaults. * @param propClass * @param globalClass * @param defaultClass */ const getLinkClass = (propClass, globalClass, defaultClass) => propClass != null ? propClass : globalClass != null ? globalClass : defaultClass; const RouterViewImpl = /* @__PURE__ */ defineComponent({ name: "RouterView", inheritAttrs: false, props: { name: { type: String, default: "default" }, route: Object }, compatConfig: { MODE: 3 }, setup(props, { attrs, slots }) { const injectedRoute = inject(routerViewLocationKey); const routeToDisplay = computed(() => props.route || injectedRoute.value); const injectedDepth = inject(viewDepthKey, 0); const depth = computed(() => { let initialDepth = unref(injectedDepth); const { matched } = routeToDisplay.value; let matchedRoute; while ((matchedRoute = matched[initialDepth]) && !matchedRoute.components) initialDepth++; return initialDepth; }); const matchedRouteRef = computed(() => routeToDisplay.value.matched[depth.value]); provide(viewDepthKey, computed(() => depth.value + 1)); provide(matchedRouteKey, matchedRouteRef); provide(routerViewLocationKey, routeToDisplay); const viewRef = ref(); watch(() => [ viewRef.value, matchedRouteRef.value, props.name ], ([instance, to, name], [oldInstance, from, oldName]) => { if (to) { to.instances[name] = instance; if (from && from !== to && instance && instance === oldInstance) { if (!to.leaveGuards.size) to.leaveGuards = from.leaveGuards; if (!to.updateGuards.size) to.updateGuards = from.updateGuards; } } if (instance && to && (!from || !isSameRouteRecord(to, from) || !oldInstance)) (to.enterCallbacks[name] || []).forEach((callback) => callback(instance)); }, { flush: "post" }); return () => { const route = routeToDisplay.value; const currentName = props.name; const matchedRoute = matchedRouteRef.value; const ViewComponent = matchedRoute && matchedRoute.components[currentName]; if (!ViewComponent) return normalizeSlot(slots.default, { Component: ViewComponent, route }); const routePropsOption = matchedRoute.props[currentName]; const routeProps = routePropsOption ? routePropsOption === true ? route.params : typeof routePropsOption === "function" ? routePropsOption(route) : routePropsOption : null; const onVnodeUnmounted = (vnode) => { if (vnode.component.isUnmounted) matchedRoute.instances[currentName] = null; }; const component = h(ViewComponent, assign({}, routeProps, attrs, { onVnodeUnmounted, ref: viewRef })); return normalizeSlot(slots.default, { Component: component, route }) || component; }; } }); function normalizeSlot(slot, data) { if (!slot) return null; const slotContent = slot(data); return slotContent.length === 1 ? slotContent[0] : slotContent; } /** * Component to display the current route the user is at. */ const RouterView = RouterViewImpl; /** * Creates a Router instance that can be used by a Vue app. * * @param options - {@link RouterOptions} */ function createRouter(options) { const matcher = createRouterMatcher(options.routes, options); const parseQuery$1 = options.parseQuery || parseQuery; const stringifyQuery$1 = options.stringifyQuery || stringifyQuery; const routerHistory = options.history; const beforeGuards = useCallbacks(); const beforeResolveGuards = useCallbacks(); const afterGuards = useCallbacks(); const currentRoute = shallowRef(START_LOCATION_NORMALIZED); let pendingLocation = START_LOCATION_NORMALIZED; if (isBrowser && options.scrollBehavior && "scrollRestoration" in history) history.scrollRestoration = "manual"; const normalizeParams = applyToParams.bind(null, (paramValue) => "" + paramValue); const encodeParams = applyToParams.bind(null, encodeParam); const decodeParams = applyToParams.bind(null, decode); function addRoute(parentOrRoute, route) { let parent; let record; if (isRouteName(parentOrRoute)) { parent = matcher.getRecordMatcher(parentOrRoute); record = route; } else record = parentOrRoute; return matcher.addRoute(record, parent); } function removeRoute(name) { const recordMatcher = matcher.getRecordMatcher(name); if (recordMatcher) matcher.removeRoute(recordMatcher); } function getRoutes() { return matcher.getRoutes().map((routeMatcher) => routeMatcher.record); } function hasRoute(name) { return !!matcher.getRecordMatcher(name); } function resolve(rawLocation, currentLocation) { currentLocation = assign({}, currentLocation || currentRoute.value); if (typeof rawLocation === "string") { const locationNormalized = parseURL(parseQuery$1, rawLocation, currentLocation.path); const matchedRoute$1 = matcher.resolve({ path: locationNormalized.path }, currentLocation); const href$1 = routerHistory.createHref(locationNormalized.fullPath); return assign(locationNormalized, matchedRoute$1, { params: decodeParams(matchedRoute$1.params), hash: decode(locationNormalized.hash), redirectedFrom: void 0, href: href$1 }); } let matcherLocation; if (rawLocation.path != null) matcherLocation = assign({}, rawLocation, { path: parseURL(parseQuery$1, rawLocation.path, currentLocation.path).path }); else { const targetParams = assign({}, rawLocation.params); for (const key in targetParams) if (targetParams[key] == null) delete targetParams[key]; matcherLocation = assign({}, rawLocation, { params: encodeParams(targetParams) }); currentLocation.params = encodeParams(currentLocation.params); } const matchedRoute = matcher.resolve(matcherLocation, currentLocation); const hash = rawLocation.hash || ""; matchedRoute.params = normalizeParams(decodeParams(matchedRoute.params)); const fullPath = stringifyURL(stringifyQuery$1, assign({}, rawLocation, { hash: encodeHash(hash), path: matchedRoute.path })); const href = routerHistory.createHref(fullPath); return assign({ fullPath, hash, query: stringifyQuery$1 === stringifyQuery ? normalizeQuery(rawLocation.query) : rawLocation.query || {} }, matchedRoute, { redirectedFrom: void 0, href }); } function locationAsObject(to) { return typeof to === "string" ? parseURL(parseQuery$1, to, currentRoute.value.path) : assign({}, to); } function checkCanceledNavigation(to, from) { if (pendingLocation !== to) return createRouterError(8, { from, to }); } function push(to) { return pushWithRedirect(to); } function replace(to) { return push(assign(locationAsObject(to), { replace: true })); } function handleRedirectRecord(to) { const lastMatched = to.matched[to.matched.length - 1]; if (lastMatched && lastMatched.redirect) { const { redirect } = lastMatched; let newTargetLocation = typeof redirect === "function" ? redirect(to) : redirect; if (typeof newTargetLocation === "string") { newTargetLocation = newTargetLocation.includes("?") || newTargetLocation.includes("#") ? newTargetLocation = locationAsObject(newTargetLocation) : { path: newTargetLocation }; newTargetLocation.params = {}; } return assign({ query: to.query, hash: to.hash, params: newTargetLocation.path != null ? {} : to.params }, newTargetLocation); } } function pushWithRedirect(to, redirectedFrom) { const targetLocation = pendingLocation = resolve(to); const from = currentRoute.value; const data = to.state; const force = to.force; const replace$1 = to.replace === true; const shouldRedirect = handleRedirectRecord(targetLocation); if (shouldRedirect) return pushWithRedirect( assign(locationAsObject(shouldRedirect), { state: typeof shouldRedirect === "object" ? assign({}, data, shouldRedirect.state) : data, force, replace: replace$1 }), // keep original redirectedFrom if it exists redirectedFrom || targetLocation ); const toLocation = targetLocation; toLocation.redirectedFrom = redirectedFrom; let failure; if (!force && isSameRouteLocation(stringifyQuery$1, from, targetLocation)) { failure = createRouterError(16, { to: toLocation, from }); handleScroll( from, from, // this is a push, the only way for it to be triggered from a // history.listen is with a redirect, which makes it become a push true, // This cannot be the first navigation because the initial location // cannot be manually navigated to false ); } return (failure ? Promise.resolve(failure) : navigate(toLocation, from)).catch((error) => isNavigationFailure(error) ? isNavigationFailure( error, 2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */ ) ? error : markAsReady(error) : triggerError(error, toLocation, from)).then((failure$1) => { if (failure$1) { if (isNavigationFailure( failure$1, 2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */ )) return pushWithRedirect( // keep options assign({ replace: replace$1 }, locationAsObject(failure$1.to), { state: typeof failure$1.to === "object" ? assign({}, data, failure$1.to.state) : data, force }), // preserve the original redirectedFrom if any redirectedFrom || toLocation ); } else failure$1 = finalizeNavigation(toLocation, from, true, replace$1, data); triggerAfterEach(toLocation, from, failure$1); return failure$1; }); } /** * Helper to reject and skip all navigation guards if a new navigation happened * @param to * @param from */ function checkCanceledNavigationAndReject(to, from) { const error = checkCanceledNavigation(to, from); return error ? Promise.reject(error) : Promise.resolve(); } function runWithContext(fn) { const app = installedApps.values().next().value; return app && typeof app.runWithContext === "function" ? app.runWithContext(fn) : fn(); } function navigate(to, from) { let guards; const [leavingRecords, updatingRecords, enteringRecords] = extractChangingRecords(to, from); guards = extractComponentsGuards(leavingRecords.reverse(), "beforeRouteLeave", to, from); for (const record of leavingRecords) record.leaveGuards.forEach((guard) => { guards.push(guardToPromiseFn(guard, to, from)); }); const canceledNavigationCheck = checkCanceledNavigationAndReject.bind(null, to, from); guards.push(canceledNavigationCheck); return runGuardQueue(guards).then(() => { guards = []; for (const guard of beforeGuards.list()) guards.push(guardToPromiseFn(guard, to, from)); guards.push(canceledNavigationCheck); return runGuardQueue(guards); }).then(() => { guards = extractComponentsGuards(updatingRecords, "beforeRouteUpdate", to, from); for (const record of updatingRecords) record.updateGuards.forEach((guard) => { guards.push(guardToPromiseFn(guard, to, from)); }); guards.push(canceledNavigationCheck); return runGuardQueue(guards); }).then(() => { guards = []; for (const record of enteringRecords) if (record.beforeEnter) if (isArray$1(record.beforeEnter)) for (const beforeEnter of record.beforeEnter) guards.push(guardToPromiseFn(beforeEnter, to, from)); else guards.push(guardToPromiseFn(record.beforeEnter, to, from)); guards.push(canceledNavigationCheck); return runGuardQueue(guards); }).then(() => { to.matched.forEach((record) => record.enterCallbacks = {}); guards = extractComponentsGuards(enteringRecords, "beforeRouteEnter", to, from, runWithContext); guards.push(canceledNavigationCheck); return runGuardQueue(guards); }).then(() => { guards = []; for (const guard of beforeResolveGuards.list()) guards.push(guardToPromiseFn(guard, to, from)); guards.push(canceledNavigationCheck); return runGuardQueue(guards); }).catch((err) => isNavigationFailure( err, 8 /* ErrorTypes.NAVIGATION_CANCELLED */ ) ? err : Promise.reject(err)); } function triggerAfterEach(to, from, failure) { afterGuards.list().forEach((guard) => runWithContext(() => guard(to, from, failure))); } /** * - Cleans up any navigation guards * - Changes the url if necessary * - Calls the scrollBehavior */ function finalizeNavigation(toLocation, from, isPush, replace$1, data) { const error = checkCanceledNavigation(toLocation, from); if (error) return error; const isFirstNavigation = from === START_LOCATION_NORMALIZED; const state = !isBrowser ? {} : history.state; if (isPush) if (replace$1 || isFirstNavigation) routerHistory.replace(toLocation.fullPath, assign({ scroll: isFirstNavigation && state && state.scroll }, data)); else routerHistory.push(toLocation.fullPath, data); currentRoute.value = toLocation; handleScroll(toLocation, from, isPush, isFirstNavigation); markAsReady(); } let removeHistoryListener; function setupListeners() { if (removeHistoryListener) return; removeHistoryListener = routerHistory.listen((to, _from, info) => { if (!router.listening) return; const toLocation = resolve(to); const shouldRedirect = handleRedirectRecord(toLocation); if (shouldRedirect) { pushWithRedirect(assign(shouldRedirect, { replace: true, force: true }), toLocation).catch(noop); return; } pendingLocation = toLocation; const from = currentRoute.value; if (isBrowser) saveScrollPosition(getScrollKey(from.fullPath, info.delta), computeScrollPosition()); navigate(toLocation, from).catch((error) => { if (isNavigationFailure( error, 12 /* ErrorTypes.NAVIGATION_CANCELLED */ )) return error; if (isNavigationFailure( error, 2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */ )) { pushWithRedirect( assign(locationAsObject(error.to), { force: true }), toLocation // avoid an uncaught rejection, let push call triggerError ).then((failure) => { if (isNavigationFailure( failure, 20 /* ErrorTypes.NAVIGATION_DUPLICATED */ ) && !info.delta && info.type === NavigationType.pop) routerHistory.go(-1, false); }).catch(noop); return Promise.reject(); } if (info.delta) routerHistory.go(-info.delta, false); return triggerError(error, toLocation, from); }).then((failure) => { failure = failure || finalizeNavigation( // after navigation, all matched components are resolved toLocation, from, false ); if (failure) { if (info.delta && !isNavigationFailure( failure, 8 /* ErrorTypes.NAVIGATION_CANCELLED */ )) routerHistory.go(-info.delta, false); else if (info.type === NavigationType.pop && isNavigationFailure( failure, 20 /* ErrorTypes.NAVIGATION_DUPLICATED */ )) routerHistory.go(-1, false); } triggerAfterEach(toLocation, from, failure); }).catch(noop); }); } let readyHandlers = useCallbacks(); let errorListeners = useCallbacks(); let ready; /** * Trigger errorListeners added via onError and throws the error as well * * @param error - error to throw * @param to - location we were navigating to when the error happened * @param from - location we were navigating from when the error happened * @returns the error as a rejected promise */ function triggerError(error, to, from) { markAsReady(error); const list = errorListeners.list(); if (list.length) list.forEach((handler) => handler(error, to, from)); else console.error(error); return Promise.reject(error); } function isReady() { if (ready && currentRoute.value !== START_LOCATION_NORMALIZED) return Promise.resolve(); return new Promise((resolve$1, reject) => { readyHandlers.add([resolve$1, reject]); }); } function markAsReady(err) { if (!ready) { ready = !err; setupListeners(); readyHandlers.list().forEach(([resolve$1, reject]) => err ? reject(err) : resolve$1()); readyHandlers.reset(); } return err; } function handleScroll(to, from, isPush, isFirstNavigation) { const { scrollBehavior } = options; if (!isBrowser || !scrollBehavior) return Promise.resolve(); const scrollPosition = !isPush && getSavedScrollPosition(getScrollKey(to.fullPath, 0)) || (isFirstNavigation || !isPush) && history.state && history.state.scroll || null; return nextTick().then(() => scrollBehavior(to, from, scrollPosition)).then((position) => position && scrollToPosition(position)).catch((err) => triggerError(err, to, from)); } const go = (delta) => routerHistory.go(delta); let started; const installedApps = new Set(); const router = { currentRoute, listening: true, addRoute, removeRoute, clearRoutes: matcher.clearRoutes, hasRoute, getRoutes, resolve, options, push, replace, go, back: () => go(-1), forward: () => go(1), beforeEach: beforeGuards.add, beforeResolve: beforeResolveGuards.add, afterEach: afterGuards.add, onError: errorListeners.add, isReady, install(app) { const router$1 = this; app.component("RouterLink", RouterLink); app.component("RouterView", RouterView); app.config.globalProperties.$router = router$1; Object.defineProperty(app.config.globalProperties, "$route", { enumerable: true, get: () => unref(currentRoute) }); if (isBrowser && !started && currentRoute.value === START_LOCATION_NORMALIZED) { started = true; push(routerHistory.location).catch((err) => {}); } const reactiveRoute = {}; for (const key in START_LOCATION_NORMALIZED) Object.defineProperty(reactiveRoute, key, { get: () => currentRoute.value[key], enumerable: true }); app.provide(routerKey, router$1); app.provide(routeLocationKey, shallowReactive(reactiveRoute)); app.provide(routerViewLocationKey, currentRoute); const unmountApp = app.unmount; installedApps.add(app); app.unmount = function() { installedApps.delete(app); if (installedApps.size < 1) { pendingLocation = START_LOCATION_NORMALIZED; removeHistoryListener && removeHistoryListener(); removeHistoryListener = null; currentRoute.value = START_LOCATION_NORMALIZED; started = false; ready = false; } unmountApp(); }; } }; function runGuardQueue(guards) { return guards.reduce((promise, guard) => promise.then(() => runWithContext(guard)), Promise.resolve()); } return router; } function extractChangingRecords(to, from) { const leavingRecords = []; const updatingRecords = []; const enteringRecords = []; const len = Math.max(from.matched.length, to.matched.length); for (let i = 0; i < len; i++) { const recordFrom = from.matched[i]; if (recordFrom) if (to.matched.find((record) => isSameRouteRecord(record, recordFrom))) updatingRecords.push(recordFrom); else leavingRecords.push(recordFrom); const recordTo = to.matched[i]; if (recordTo) { if (!from.matched.find((record) => isSameRouteRecord(record, recordTo))) enteringRecords.push(recordTo); } } return [ leavingRecords, updatingRecords, enteringRecords ]; } /** * Returns the router instance. Equivalent to using `$router` inside * templates. */ function useRouter() { return inject(routerKey); } /** * Returns the current route location. Equivalent to using `$route` inside * templates. */ function useRoute(_name) { return inject(routeLocationKey); } export { createApp, createRouter, createWebHashHistory, useRoute, useRouter, vModelCheckbox, vModelText, vShow, withKeys };