diff --git a/demo/js/base/tree/demo.part_tree.js b/demo/js/base/tree/demo.part_tree.js index f24ff62b5..60bc17f81 100644 --- a/demo/js/base/tree/demo.part_tree.js +++ b/demo/js/base/tree/demo.part_tree.js @@ -1,4 +1,5 @@ -import { shortcut, Widget, TreeView, isNull } from "@/core"; +import { shortcut, Widget, isNull } from "@/core"; +import { TreeView } from "@/case"; @shortcut() export class Func extends Widget { diff --git a/demo/js/case/triggers/demo.select_text_trigger.js b/demo/js/case/triggers/demo.select_text_trigger.js index 771e11f3a..7dc5da582 100644 --- a/demo/js/case/triggers/demo.select_text_trigger.js +++ b/demo/js/case/triggers/demo.select_text_trigger.js @@ -1,4 +1,4 @@ -import { shortcut, Widget } from "@/core"; +import { shortcut, Widget, createWidget } from "@/core"; @shortcut() export class Func extends Widget { diff --git a/demo/js/core/abstract/demo.virtual_list.js b/demo/js/core/abstract/demo.virtual_list.js index fc2a9e4f7..c358d7941 100644 --- a/demo/js/core/abstract/demo.virtual_list.js +++ b/demo/js/core/abstract/demo.virtual_list.js @@ -1,4 +1,4 @@ -import { shortcut, Widget } from "@/core"; +import { shortcut, Widget, map, extend } from "@/core"; @shortcut() export class Func extends Widget { @@ -13,9 +13,9 @@ export class Func extends Widget { extend({}, item, { type: "bi.label", height: 30, - text: `${i + 1}.${item.text}`, + text: `${i + 1}.${item.text}` }) - ), + ) }; } } diff --git a/es6.babel.js b/es6.babel.js new file mode 100644 index 000000000..b2f93ed08 --- /dev/null +++ b/es6.babel.js @@ -0,0 +1,19 @@ +module.exports = { + plugins: [ + [ + "@babel/plugin-proposal-decorators", + { + legacy: true + } + ], + [ + "module-resolver", + { + root: ["./src"], + alias: { + "@": "./src" + } + } + ] + ] +}; diff --git a/es6.js b/es6.js deleted file mode 100644 index 82231feec..000000000 --- a/es6.js +++ /dev/null @@ -1,538 +0,0 @@ -const fs = require("fs"); -const path = require("path"); -const prettier = require("prettier"); -const { exec } = require("child_process"); -const { search, initDepts, depts } = require("./es6.xtype"); -const lodash = require("lodash"); -const DEPTS = depts; - -// const XTYPE_ONLY = false; -// const THIS_REPLACE = false; - -const ConflictImport = []; -const CircularDependency = []; - -function objHaveFunction(obj) { - if (obj === null) { - return false; - } - - return Object.keys(obj).some(key => { - const value = obj[key]; - if (typeof value === "object") { - return objHaveFunction(value); - } else if (typeof value === "function") { - return true; - } - - return false; - }); -} - -function parserImport(code) { - const reg = /import {([\s\S]*?)} from "(.*?)";/g; - - const importMap = {}; - let regResult = reg.exec(code); - while (regResult) { - importMap[regResult[2]] = regResult[1] - .split(",") - .map(el => el.trim()) - .filter(el => el); - regResult = reg.exec(code); - } - - return importMap; -} - -async function saveAndFixCode(path, code) { - let _code = code; - if (!code) { - _code = fs.readFileSync(path).toString(); - } - const prettierCode = prettier.format(_code, { - tabWidth: 4, - parser: "babel", - printWidth: 120, - }); - fs.writeFileSync(path, prettierCode); - - new Promise(res => { - exec(`yarn eslint --fix ${path}`, () => { - res(); - }); - }); -} - -function divideFile(srcName) { - const targetPath = srcName.match(/.*\//g); - const sourceCode = fs.readFileSync(srcName).toString(); - const splitSourceCode = sourceCode.match(/[\.\s]{1}[^\s\.]+?\s=\sBI\.inherit\([^]*?BI\.shortcut.*\;/g); - const newFileNames = []; - for (let i = 0; i < splitSourceCode.length; i++) { - // 去除开头的 空格 或者 . - const newCode = splitSourceCode[i].slice(1); - // 匹配第一个等号之前的组件名 - const componentName = /BI\.shortcut\("(.*)"/.exec(newCode)[1]; - // 文件名转化 ButtonIcon => demo.button.icon.js - const demoFileName = componentName + '.js'; - newFileNames.push(demoFileName); - // 代码 componentName 前面加上 BI. - const fileCode = 'BI.' + newCode; - - // 规范最后一行的组件为 BI.Button - const targetComponet = /(BI\..*)\s=/.exec(fileCode)[1]; - // 最后一行的内容 - const replaceContext = /BI\.shortcut.*;/.exec(fileCode)[0]; - // 替换 - const finalCode = fileCode.replace(replaceContext,`BI.shortcut("${componentName}", ${targetComponet});`) - - // 创建新文件 - fs.writeFileSync(targetPath + demoFileName, finalCode); - } - return newFileNames; -} - -// const target = []; - -// 加载模块 -const loader = { - // G: { "@/core": { shortcut: true } }, - load(srcName, module) { - const G = loader.G; - // if (target.indexOf(module) >= 0) { - // G["@/core"][module] = true; - - // return true; - // } - if (module.startsWith('"bi.')) { - const key = search(srcName, module); - if (key) { - if (!G[key]) { - G[key] = {}; - } - const clzName = depts[module].clzName; - G[key][clzName] = true; - } - - return !!key; - } else { - const key = search(srcName, module); - if (key) { - if (!G[key]) { - G[key] = {}; - } - G[key][module] = true; - } - - return !!key; - } - }, -}; - -async function handleFile(srcName) { - await saveAndFixCode(srcName); - // 全局状态回归 - let G = (loader.G = {}); - - const sourceCode = fs.readFileSync(srcName).toString(); - if (sourceCode.match(/shortcut/g).length > 1) { - console.log('该文件存在多处BI.shorcut, 需要拆分...'); - const newTargets = divideFile(srcName); - newTargets.forEach(name => console.log(name)); - return; - } - - const result = /BI\.(.*?)\s=\sBI\.inherit\(/.exec(sourceCode); - - if (!result) { - // console.log(`已经es6过,替换 xtype => ${srcName}`); - if (!/export class/.test(sourceCode)) { - // console.log("忽略文件", srcName); - - return; - } - - // 处理 xtype - const noXtypeCode = sourceCode.replace(/type:\s?"bi\.(.*?)"/g, v => { - const matchedSentence = v.replace(/type:\s?/, ""); - const loadSuccess = loader.load(srcName, matchedSentence); - if (loadSuccess) { - const clzName = depts[matchedSentence].clzName; - - return `type: ${clzName}.xtype`; - } else { - console.log(`xtype 替换失败 ${matchedSentence} `); - - return v; - } - }); - // 识别 import - const importMap = parserImport(noXtypeCode); - - // 合并原来的 import 到 G - lodash.forEach(importMap, (depts, module) => { - depts.forEach(dept => { - if (!G[module]) { - G[module] = {}; - } - - G[module][dept] = true; - }); - }); - - - // 合并包 - const crossPackages = fs.readdirSync("src").map(el => `@/${el}`); - lodash.forEach(G, (depts, module) => { - crossPackages.forEach(crosspackage => { - if (module.indexOf(crosspackage.replace(/^@\//, "")) >= 0) { - if (!G[crosspackage]) { - G[crosspackage] = {}; - } - Object.assign(G[crosspackage], depts); - } - }); - }); - const tmpG = {}; - lodash.forEach(G, (depts, module) => { - const flag = lodash.some( - crossPackages, - crosspackage => - module.indexOf(crosspackage) >= 0 && !module.startsWith("@"), - ); - if (!flag) { - tmpG[module] = depts; - } - }); - - let circle = false; - // 处理循环依赖,base: ["@/case", "@/base", "@/widget"] 等于 base 不能直接引数组内的包 - const forbiddenCrossRules = { - base: ["@/case", "@/base", "@/widget"], - "case": ["@/case", "@/widget"], - widget: ["@/widget"], - component: ["@/component"], - core: ["@/core", "@base", "@/widget", "@/case"], - }; - - const forbiddenKeys = []; - const circleG = {}; - - lodash.forEach(G, (depts, module) => { - // 找出 rule - const packages = Object.keys(forbiddenCrossRules); - - let key = packages.filter( - _package => srcName.indexOf(_package) >= 0, - ); - if (key.length !== 1) { - throw new Error( - "理论不可能出现这个问题,需要 treecat 优化下找包逻辑1", - ); - } - key = key[0]; - const rule = forbiddenCrossRules[key]; - - if (lodash.includes(rule, module)) { - circle = true; - - const deptsArr = Object.keys(depts); - if (deptsArr.filter(dept => !DEPTS[dept]).length > 0) { - throw new Error( - "理论不可能出现这个问题,需要 treecat 优化下找包逻辑2", - ); - } - deptsArr - .filter(dept => DEPTS[dept]) - .forEach(dept => { - const value = `@${DEPTS[dept].replace(path.resolve("src"), "").replace(/\\/g, "/").replace(/\.js$/, "")}`; - if (!tmpG[value]) { - tmpG[value] = {}; - } - tmpG[value][dept] = true; - }); - forbiddenKeys.push(module); - } - }); - Object.assign(tmpG, circleG); - forbiddenKeys.forEach(key => { - delete tmpG[key]; - }); - - - // 较验手工 import 错误 - const map = {}; - let conflict = false; - - lodash.forEach(tmpG, (depts, module) => { - lodash.forEach(depts, (_, _import) => { - if (map[_import] && map[_import] !== module) { - conflict = true; - } - - map[_import] = module; - }); - }); - - conflict && ConflictImport.push(srcName); - circle && CircularDependency.push(srcName); - - - - G = tmpG; - - const noImportCode = noXtypeCode.replace( - /import {([\s\S]*?)} from "(.*?)";/g, - "", - ); - - let I = ""; - Object.keys(G).forEach(key => { - let moduleKey = key; - if (moduleKey === path.basename(srcName).replace(/.js$/g, "")) { - return; - } - let i = ""; - Object.keys(G[moduleKey]).forEach(key => { - i += `${key}, `; - }); - - // 必须以 . 开头 - const moduleInValid = /^[^@.]/.test(moduleKey); - if (moduleInValid) { - moduleKey = `./${moduleKey}`; - } - - I += `import {${i}} from '${moduleKey}'\n`; - }); - const code = `${I}\n${noImportCode}`; - - await saveAndFixCode(srcName, code); - - return; - } - - G["@/core"] = { shortcut: true }; - - const clzName = result[1]; - - if (!clzName) { - console.log(`${srcName} 不需要 es6 化`); - - return; - } - - const superName = /inherit\(BI\.(.*?),/.exec(sourceCode)[1]; - - // const xtype = /BI.shortcut\(\"(.*?)\"/.exec(sourceCode)[1]; - - const collection = { - "static": {}, - attr: {}, - }; - - // eslint-disable-next-line no-unused-vars - const BI = { - [clzName]: clzName, - inherit(_, options) { - collection.methods = Object.keys(options) - .filter(key => typeof options[key] === "function") - .map(key => options[key]); - Object.keys(options) - .filter(key => typeof options[key] !== "function") - .forEach(key => { - collection.attr[key] = options[key]; - }); - - return collection.static; - }, - extend(targetClz, o) { - Object.assign(collection.static, o); - }, - shortcut(xtype) { - collection.xtype = xtype; - }, - }; - - // eslint-disable-next-line no-eval - eval(sourceCode); - - let M = ""; - let E = ""; - let I = ""; - let A = ""; - - loader.load(srcName, superName); - - Object.keys(collection.attr).forEach(key => { - const value = collection.attr[key]; - if (typeof value === "function" || typeof value === "number") { - A += `\t${key} = ${value}\n`; - } else if (typeof value === "string") { - A += `\t${key} = "${value}"\n`; - } else if (typeof value === "object") { - if (objHaveFunction(value)) { - throw new Error("G"); - } else { - A += `\t${key} = ${JSON.stringify(value)}\n`; - } - } - }); - - // 静态方法 - Object.keys(collection.static).forEach(key => { - // console.log(key, collection.static[key], typeof collection.static[key]) - const value = collection.static[key]; - if (typeof value === "function" || typeof value === "number") { - E += `\tstatic ${key} = ${value}\n`; - } else if (typeof value === "string") { - E += `\tstatic ${key} = "${value}"\n`; - } else if (typeof value === "object") { - if (objHaveFunction(value)) { - throw new Error("G"); - } else { - E += `\tstatic ${key} = ${JSON.stringify(value)}\n`; - } - } - }); - - // 对函数进行替换 - collection.methods.forEach(el => { - let f = `${el.toString().replace(/^function/, el.name)}\n`; - - // 换 BI.Button.superclass,就说能不能跑吧 - for (let i = 0; i < 100; i++) { - f = f.replace(`BI.${clzName}.superclass`, "super"); - } - // 换 super._defaultConfig - f = f.replace( - /super\._defaultConfig\.apply\(this,\sarguments\)/g, - "super._defaultConfig(...arguments)", - ); - // 换 super.xxx.apply - f = f.replace(/super\.(.*?)\.apply\(this,\sarguments\)/g, a => { - const f = /super\.(.*?)\.apply\(this,\sarguments\)/.exec(a); - - return `super.${f[1]}(...arguments)`; - }); - - // 尝试换掉所有的 BI.xxx. BI.xxx( BI.xxx[空白] - f = f.replace(/BI\.(.*?)(\.|\(|\s|,)/g, matchedSentence => { - const match = /BI\.(.*?)(\.|\(|\s|,)/.exec(matchedSentence); - const target = match[1]; - const end = match[2]; - // 尝试加载 target - const loadSuccess = loader.load(srcName, target); - if (loadSuccess) { - return target + end; - } else { - console.log(`BI 变量替换失败 BI.${target}`); - - return matchedSentence; - } - }); - - // 尝试对 xtype 进行替换 - f = f.replace(/"bi\.(.*?)"/g, matchedSentence => { - const loadSuccess = loader.load(srcName, matchedSentence); - if (loadSuccess) { - const clzName = depts[matchedSentence].clzName; - - return `${clzName}.xtype`; - } else { - // console.log(`(没事) xtype 替换失败 ${matchedSentence} `); - - return matchedSentence; - } - }); - - M += `${f}\n`; - }); - - if (!collection.xtype) { - delete G["@/core"].shortcut; - } - - Object.keys(G).forEach(key => { - let moduleKey = key; - if (moduleKey === path.basename(srcName).replace(/.js$/g, "")) { - return; - } - let i = ""; - Object.keys(G[moduleKey]).forEach(key => { - i += `${key}, `; - }); - - // 必须以 . 开头 - const moduleInValid = /^[^@.]/.test(moduleKey); - if (moduleInValid) { - moduleKey = `./${moduleKey}`; - } - - I += `import {${i}} from '${moduleKey}'\n`; - }); - - const outputCode = ` -${I} - -${collection.xtype ? "@shortcut()" : ""} -export class ${clzName} extends ${superName} { -${collection.xtype ? `static xtype = "${collection.xtype}"` : ""} - -${A} - -${E} - -${M} -} -`; - await saveAndFixCode(srcName, outputCode); - - return clzName; -} - -async function traverse(srcName) { - if (srcName.indexOf("__test__") >= 0) return; - - if (srcName.endsWith(".js")) { - try { - return await handleFile(srcName); - } catch (error) { - console.log(`文件处理失败 ${srcName} \n`); - console.error(error); - - return; - } - } else { - const stat = fs.statSync(srcName); - const flag = stat.isDirectory(); - if (flag) { - const files = fs.readdirSync(srcName); - // let indexContent = ""; - for (let i = 0; i < files.length; i++) { - const file = files[i]; - await traverse(path.resolve(srcName, file)); - // const clzName = await traverse(path.resolve(srcName, file)); - // const moduleName = path.basename(srcName).replace(/.js$/, ""); - // if (clzName) { - // indexContent += `export { ${clzName} } from '${moduleName}'\n`; - // } - } - } - } -} - -const srcName = process.argv[2]; - -initDepts().then(async () => { - await traverse(srcName); - - // 对数据处理 - ConflictImport.forEach(el => { - console.log(`导入冲突 ${el}`); - }); - CircularDependency.forEach(el => { - console.log(`出现循环依赖(已经fixed) ${el}`); - }); -}); diff --git a/es6.xtype.js b/es6.xtype.js deleted file mode 100644 index db1babcbb..000000000 --- a/es6.xtype.js +++ /dev/null @@ -1,172 +0,0 @@ -const fs = require("fs"); -const path = require("path"); -const lodash = require("lodash"); - -const depts = {}; - -async function handle(filename) { - if (path.extname(filename) !== ".js") { - return; - } - const code = fs.readFileSync(filename).toString(); - - const inheritRegResult = /BI\.(.*?)\s=\sBI\.inherit\(/.exec(code); - if (inheritRegResult) { - const clzName = inheritRegResult[1]; - depts[clzName] = filename; - } else { - // 一个文件夹里面可能有多个 xtype - const reg = /export\s+class\s+(.*?)([\s|{])([\w\W]*?)static xtype\s?=\s?((["|'])(.*?)(\5))/g; - Array.from(code.matchAll(reg)).forEach(res => { - const xtype = res[4]; - depts[xtype] = { - clzName: res[1], - clzPath: filename, - }; - }); - // 同时找一下 export - if (path.basename(filename) !== "index.js") { - const regs = [ - /export function (.*?)\(/g, - /export const (.*?) =/g, - /export class (.*?) /g, - /export \{([\w\W]*?)\}/g, - ]; - regs.forEach((reg, index) => { - Array.from(code.matchAll(reg)).forEach(el => { - depts[el[1]] = filename; - if (index === 3) { - el[1].split(",").map(el => el.trim()).forEach(key => { - depts[key] = filename; - }); - } - }); - }); - } - } -} - -function isExist(filePath) { - return fs.existsSync(filePath); -} - -async function bfs(filename) { - const stat = fs.statSync(filename); - const isDir = stat.isDirectory(); - if (isDir) { - const files = fs.readdirSync(filename); - for (let i = 0; i < files.length; i++) { - const file = files[i]; - await bfs(path.resolve(filename, file)); - } - } else { - await handle(filename); - } -} - -async function initDepts() { - // dfs 构建依赖关系 - await bfs(path.resolve("src")); - const m = {}; - lodash.forEach(depts, value => { - if (typeof value === "object") { - m[value.clzName] = value.clzPath; - } - }); - Object.assign(depts, m); -} - - -function search(src, module) { - let clzName = module; - let clzPath = depts[module]; - - if (!depts[clzName]) { - return ""; - } - - if (clzName.indexOf("\"") >= 0) { - clzName = depts[module].clzName; - clzPath = depts[module].clzPath; - } - - const dstName = path.basename(clzPath).replace(/.js$/g, ""); - const dstPath = path.normalize(clzPath).split("src")[1].split("\\").join("/").split("/"); - const srcPath = path.normalize(src).split("src")[1].split("\\").join("/").split("/"); - - // console.log("src", src); - // console.log("dst", depts[clzName]); - - dstPath.shift(); - dstPath.pop(); - srcPath.shift(); - srcPath.pop(); - - const findDstIndexPath = (dstArr, startIndex) => { - let i = startIndex; - - while (!isExist(path.resolve("src", dstArr.slice(0, i + 1).join("/"), "index.js")) && i < dstArr.length) { - i++; - } - - if (i < dstArr.length) { - return dstArr.slice(startIndex, i + 1).join("/"); - } else { - return `${dstArr.slice(startIndex).join("/")}/${dstName}`; - } - }; - - // 不同包 - if (dstPath[0] !== srcPath[0]) { - return `@/${dstPath[0]}`; - } - - // 同包 - let i = 0; - while (dstPath[i] === srcPath[i] && i < dstPath.length && i < srcPath.length) { - i++; - } - - // i 代表同名的位置 - i--; - - // 没有匹配完 - if (i < srcPath.length) { - let result = ""; - - // 回溯,向上找,回到目录 i - for (let j = srcPath.length - 1; j > i; j--) { - result += "../"; - } - - // 匹配过的下一个位置 - i++; - - if (i >= dstPath.length) { - // 越界 - return `${result}${dstName}`; - } else { - // 看看这个目录下有没有 index - return result + findDstIndexPath(dstPath, i); - } - } else if (i === srcPath.length) { - if (i === dstPath.length) { - return dstName; - } else if (i < dstPath.length) { - return findDstIndexPath(dstPath, i); - } - } -} - -// search(process.argv[2], "Text").then(res => { -// console.log(res); -// }); - -exports.initDepts = initDepts; -exports.search = search; -exports.depts = depts; - - - - - diff --git a/package.json b/package.json index 18f8b60a4..7b56d27f1 100644 --- a/package.json +++ b/package.json @@ -3,11 +3,16 @@ "version": "2.0.20230208163847", "description": "fineui", "main": "dist/fineui_without_conflict.min.js", + "module": "dist/esm/index.js", + "sideEffects": [ + "dist/esm/core/**" + ], "types": "dist/lib/index.d.ts", "bin": { "fui-cli": "./bin/cli/cli.js" }, "devDependencies": { + "@babel/cli": "^7.21.0", "@babel/core": "^7.17.4", "@fui/babel-preset-fineui": "^3.0.0", "@fui/eslint-plugin": "^1.0.19", @@ -21,6 +26,7 @@ "@typescript-eslint/parser": "2.33.0", "autoprefixer": "9.6.1", "babel-loader": "8.0.6", + "babel-plugin-module-resolver": "^5.0.0", "chai": "4.2.0", "concat": "1.0.3", "core-js": "3.3.2", @@ -66,10 +72,12 @@ "webpack:prod": "node --max_old_space_size=4096 node_modules/webpack/bin/webpack -p --progress --config=webpack/webpack.prod.js --mode production && npm run biCss && npm run jsyCss", "webpack:css": "node --max_old_space_size=4096 node_modules/webpack/bin/webpack -p --progress --config=webpack/webpack.css.js --mode production", "start": "node server.js", - "build": "npm run webpack:prod && tsc", + "build": "npm run esm && npm run webpack:prod && tsc", "postbuild": "node ./lib/postbuild/postbuild.js", "test": "karma start", "dev": "npm run webpack:dev", + "esm": "babel src -d dist\\esm --config-file .\\es6.babel.js", + "esm:dev": "babel src -d dist\\esm --config-file .\\es6.babel.js -w", "prepublishToPrivate": "npm run build && node ./lib/prepublish/prepublish.js", "publishToPrivate": "npm publish", "postpublishToPrivate": " node ./lib/postpublish/postpublish.js", @@ -91,6 +99,7 @@ "author": "fanruan", "license": "MIT", "dependencies": { + "@popperjs/core": "2.11.6", "@types/yargs": "17.0.13", "jquery": "3.6.3", "yargs": "17.6.2" diff --git a/rollup.config.js b/rollup.config.js index fabb1416b..fe5699412 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -2,38 +2,64 @@ const babel = require("@rollup/plugin-babel"); const alias = require("@rollup/plugin-alias"); const resolve = require("@rollup/plugin-node-resolve"); const commonjs = require("@rollup/plugin-commonjs"); +const sizes = require("rollup-plugin-sizes"); const path = require("path"); -/** - * todo: 删除根目录下的 babel.config.js,然后移入到这个文件 - */ - -const input = "src/index.js"; - module.exports = [ { - input, + input: path.resolve(__dirname, "src/index.js"), output: [ { file: "dist/fineui.esm.js", format: "esm", sourcemap: true, - }, + } ], plugins: [ alias({ entries: [ - { find: "@", replacement: path.resolve(__dirname, "src") }, - ], + { find: "@", replacement: path.resolve(__dirname, "src") } + ] }), resolve(), babel({ babelHelpers: "inline", plugins: [ - ["@babel/plugin-proposal-decorators", { legacy: true }], - ], + ["@babel/plugin-proposal-decorators", { legacy: true }] + ] }), - commonjs(), - ], + commonjs() + ] }, + { + input: path.resolve(__dirname, "src/bundle.js"), + output: [ + { + file: "dist/fineui.min.js", + sourcemap: true, + format: "umd" + } + ], + plugins: [ + alias({ + entries: [ + { find: "@", replacement: path.resolve(__dirname, "src") } + ] + }), + resolve(), + babel({ + babelHelpers: "inline", + plugins: [ + ["@babel/plugin-proposal-decorators", { legacy: true }] + ] + }), + // babel({ + // babelHelpers: "runtime", + // presets: [babelConfig] + // }), + commonjs(), + sizes(), + // terser() + ] + } ]; diff --git a/src/base/behavior/0.behavior.js b/src/base/behavior/0.behavior.js new file mode 100644 index 000000000..99ef2f633 --- /dev/null +++ b/src/base/behavior/0.behavior.js @@ -0,0 +1,17 @@ +/** + * guy + * 行为控件 + */ +import { OB, extend } from "@/core"; + +export class Behavior extends OB { + _defaultConfig() { + return extend(super._defaultConfig(...arguments), { + rule: () => true, + }); + } + + doBehavior() { + + } +} diff --git a/src/base/behavior/behavior.highlight.js b/src/base/behavior/behavior.highlight.js new file mode 100644 index 000000000..bd60ac46e --- /dev/null +++ b/src/base/behavior/behavior.highlight.js @@ -0,0 +1,34 @@ +/** + * guy + */ +import { isFunction, each } from "@/core"; +import { Single } from "../single/0.single"; +import { Behavior } from "./0.behavior"; + +export class HighlightBehavior extends Behavior { + doBehavior(items) { + const args = Array.prototype.slice.call(arguments, 1), + { rule } = this.options; + each(items, (i, item) => { + if (item instanceof Single) { + const rules = rule(item.getValue(), item); + + function doBe(run) { + if (run === true) { + item.doHighLight && item.doHighLight(...args); + } else { + item.unHighLight && item.unHighLight(...args); + } + } + + if (isFunction(rules)) { + rules(doBe); + } else { + doBe(rules); + } + } else { + item.doBehavior && item.doBehavior(...args); + } + }); + } +} diff --git a/src/base/behavior/behavior.redmark.js b/src/base/behavior/behavior.redmark.js new file mode 100644 index 000000000..9e770195a --- /dev/null +++ b/src/base/behavior/behavior.redmark.js @@ -0,0 +1,25 @@ +/** + * guy + * 标红行为 + */ +import { Behavior } from "./0.behavior"; +import { each } from "@/core"; +import { Single } from "../single/0.single"; + +export class RedMarkBehavior extends Behavior { + doBehavior(items) { + const args = Array.prototype.slice.call(arguments, 1), + { rule } = this.options; + each(items, (i, item) => { + if (item instanceof Single) { + if (rule(item.getValue(), item)) { + item.doRedMark && item.doRedMark(...args); + } else { + item.doRedMark && item.unRedMark(...args); + } + } else { + item.doBehavior && item.doBehavior(...args); + } + }); + } +} diff --git a/src/base/behavior/index.js b/src/base/behavior/index.js new file mode 100644 index 000000000..52d5f6499 --- /dev/null +++ b/src/base/behavior/index.js @@ -0,0 +1,26 @@ + +import { HighlightBehavior } from "./behavior.highlight"; +import { RedMarkBehavior } from "./behavior.redmark"; + +export const BehaviorFactory = { + createBehavior (key, options) { + let Behavior; + switch (key) { + case "highlight": + Behavior = HighlightBehavior; + break; + case "redmark": + Behavior = RedMarkBehavior; + break; + default: + } + + return new Behavior(options); + }, +}; + +export { Behavior } from "./0.behavior"; +export { + HighlightBehavior, + RedMarkBehavior +}; diff --git a/src/base/combination/group.button.js b/src/base/combination/group.button.js index 99437247c..a88c79b1f 100644 --- a/src/base/combination/group.button.js +++ b/src/base/combination/group.button.js @@ -26,10 +26,10 @@ import { deepContains, has, any, - BehaviorFactory, Events } from "@/core"; import { TextButton } from "../single"; +import { BehaviorFactory } from "../behavior"; /** * Created by GUY on 2015/6/26. diff --git a/src/bundle.js b/src/bundle.js index 75b93152f..4626a6e37 100644 --- a/src/bundle.js +++ b/src/bundle.js @@ -1,4 +1,14 @@ import * as fineui from "./index"; import { shortcut, provider } from "@/core/5.inject"; +import { _global } from "@/core/0.foundation"; +import * as Decorators from "@/core/decorator"; -Object.assign(BI, fineui, { shortcut, provider }); +_global.BI = _global.BI || {}; +_global.BI.Decorators = Decorators; + +_global.Fix = fineui.Fix; +_global._ = fineui._; + +_global.$ = fineui.$; + +Object.assign(_global.BI, fineui, { shortcut, provider }); diff --git a/src/case/ztree/jquery.ztree.excheck-3.5.js b/src/case/ztree/jquery.ztree.excheck-3.5.js index 9efb23a9d..24f66b5ac 100644 --- a/src/case/ztree/jquery.ztree.excheck-3.5.js +++ b/src/case/ztree/jquery.ztree.excheck-3.5.js @@ -489,7 +489,7 @@ import $ from "jquery"; r = consts.radio, checkboxType = setting.check.chkboxType; var notEffectByOtherNode = checkboxType.Y === "" && checkboxType.N === ""; - fullStyle = ""; + var fullStyle = ""; if (node.chkDisabled === true) { fullStyle = c.DISABLED; } else if (node.halfCheck) { diff --git a/src/core/1.lodash.js b/src/core/1.lodash.js index 269718d61..ca3a3ade0 100644 --- a/src/core/1.lodash.js +++ b/src/core/1.lodash.js @@ -10356,6 +10356,6 @@ freeExports._ = lodash; } else { // Export to the global object. - BI._ = lodash; + // BI._ = lodash; } }.call(this)); diff --git a/src/core/decorator.js b/src/core/decorator.js index 0e5d5e097..73dfc469d 100644 --- a/src/core/decorator.js +++ b/src/core/decorator.js @@ -1,6 +1,11 @@ // export * from "../../typescript/core/decorator/decorator.ts"; -import { shortcut as biShortcut, provider as biProvider } from "./5.inject"; +import { + shortcut as biShortcut, + provider as biProvider, + model as biModel, + Models +} from "./5.inject"; /** * 注册widget @@ -19,3 +24,68 @@ export function provider() { biProvider(Target.xtype, Target); }; } + +/** + * 注册model + */ +export function model() { + return function decorator(Target) { + biModel(Target.xtype, Target); + }; +} + +/** + * 类注册_store属性 + * @param Model model类 + * @param opts 额外条件 + */ +export function store(Model, opts) { + return function classDecorator(constructor) { + return class extends constructor { + _store() { + const props = opts.props ? opts.props.apply(this) : undefined; + + return Models.getModel(Model.xtype, props); + } + }; + }; +} + +/** + * 注册mixin + * ie8下不能使用 + */ +export function mixin() { + return function decorator(Target) { + const mixin = {}; + + Object.getOwnPropertyNames(Target.prototype).forEach(name => { + if (name === "constructor") { + return; + } + + mixin[name] = Target.prototype[name]; + }); + + Fix.mixin(Target.xtype, mixin); + }; +} + +/** + * 类注册mixins属性 + * ie8下不能使用 + * @param Mixins + */ +export function mixins(...Mixins) { + return function classDecorator(constructor) { + const mixins = []; + + Mixins.forEach(mixin => { + mixin.xtype && mixins.push(mixin.xtype); + }); + + return class extends constructor { + mixins = mixins; + }; + }; +} diff --git a/src/core/index.js b/src/core/index.js index 78a4d5090..134ab617d 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -22,7 +22,7 @@ export * from "./5.inject"; export * from "./6.plugin"; export * from "./system"; export * from "./action"; -export * from "./behavior"; +// export * from "./behavior"; // export * from "./controller"; export * from "./func"; export * from "./structure"; diff --git a/src/core/platform/web/config.js b/src/core/platform/web/config.js index 6b75cb0f4..c9e24139c 100644 --- a/src/core/platform/web/config.js +++ b/src/core/platform/web/config.js @@ -3,6 +3,21 @@ import { Providers } from "../../5.inject"; import { Plugin } from "../../6.plugin"; import { isSupportCss3, isIE, getIEVersion } from "../../platform/web"; import { HorizontalAlign, VerticalAlign } from "../../constant"; +import { FlexCenterLayout, FlexHorizontalLayout, VerticalLayout, + FlexVerticalLayout, TdLayout, InlineLayout, TableAdaptLayout, + FloatHorizontalFillLayout, ResponsiveInlineLayout, + InlineCenterAdaptLayout, FlexVerticalCenterAdapt, + InlineVerticalAdaptLayout, HorizontalAutoLayout, + TableAdaptLayout, FlexHorizontalCenter, + InlineHorizontalAdaptLayout, TableLayout, + AutoVerticalTapeLayout, VTapeLayout, + HorizontalFillLayout, VerticalFillLayout, + FlexLeftRightVerticalAdaptLayout, ResponsiveFlexWrapperHorizontalLayout, + FlexWrapperHorizontalLayout, ResponsiveFlexHorizontalLayout, + FlexWrapperVerticalLayout +} from "@/core/wrapper/layout/index"; +import { SystemProvider } from "@/core/system"; + // 工程配置 // 注册布局 @@ -54,25 +69,25 @@ configWidget("bi.horizontal", ob => { // return extend({}, ob, {type: "bi.table_adapt"}); // } if (supportFlex) { - return extend({}, ob, { type: "bi.flex_horizontal" }); + return extend({}, ob, { type: FlexHorizontalLayout.xtype }); } return extend({ scrollx: true, - }, ob, { type: "bi.inline" }); + }, ob, { type: InlineLayout.xtype }); }); configWidget("bi.vertical", ob => { if (ob.horizontalAlign === HorizontalAlign.Left || ob.horizontalAlign === HorizontalAlign.Right) { if (isSupportFlex()) { - return extend({}, ob, { type: "bi.flex_vertical" }); + return extend({}, ob, { type: FlexVerticalLayout.xtype }); } return extend({}, ob, { horizontalAlign: HorizontalAlign.Stretch, - type: "bi.vertical", + type: VerticalLayout.xtype, items: map(ob.items, (i, item) => { return { - type: "bi.inline", + type: InlineLayout.xtype, horizontalAlign: ob.horizontalAlign, items: [item], }; @@ -83,7 +98,7 @@ configWidget("bi.vertical", ob => { if (isSupportFlex()) { return extend({ horizontalAlign: HorizontalAlign.Stretch, - }, ob, { type: "bi.flex_vertical" }); + }, ob, { type: FlexVerticalLayout.xtype }); } } @@ -113,15 +128,15 @@ configWidget("bi.inline", ob => { if ((ob.scrollable !== true && ob.scrollx !== true) || ob.horizontalAlign === HorizontalAlign.Stretch) { return extend({ verticalAlign: VerticalAlign.Top, - }, ob, { type: "bi.horizontal_float_fill" }); + }, ob, { type: FloatHorizontalFillLayout.xtype }); } return extend({ horizontalAlign: HorizontalAlign.Stretch, - }, ob, { type: "bi.table_adapt" }); + }, ob, { type: TableAdaptLayout.xtype }); } - if (Providers.getProvider("bi.provider.system").getResponsiveMode()) { - return extend({}, ob, { type: "bi.responsive_inline" }); + if (Providers.getProvider(SystemProvider.xtype).getResponsiveMode()) { + return extend({}, ob, { type: ResponsiveInlineLayout.xtype }); } return ob; @@ -131,10 +146,10 @@ configWidget("bi.center_adapt", ob => { // var isAdapt = !ob.horizontalAlign || ob.horizontalAlign === HorizontalAlign.Center || ob.horizontalAlign === HorizontalAlign.Stretch; // if (!isAdapt || justOneItem) { if (supportFlex) { - return extend({}, ob, { type: "bi.flex_center_adapt" }); + return extend({}, ob, { type: FlexCenterLayout.xtype }); } - return extend({}, ob, { type: "bi.inline_center_adapt" }); + return extend({}, ob, { type: InlineCenterAdaptLayout.xtype }); // } // return ob; }); @@ -143,10 +158,10 @@ configWidget("bi.vertical_adapt", ob => { // var isAdapt = ob.horizontalAlign === HorizontalAlign.Center || ob.horizontalAlign === HorizontalAlign.Stretch; // if (!isAdapt || justOneItem) { if (supportFlex) { - return extend({}, ob, { type: "bi.flex_vertical_center_adapt" }); + return extend({}, ob, { type: FlexVerticalCenterAdapt.xtype }); } - return extend({}, ob, { type: "bi.inline_vertical_adapt" }); + return extend({}, ob, { type: InlineVerticalAdaptLayout.xtype }); // } // return ob; }); @@ -156,7 +171,7 @@ configWidget("bi.horizontal_adapt", ob => { const isAdapt = !ob.horizontalAlign || ob.horizontalAlign === HorizontalAlign.Center || ob.horizontalAlign === HorizontalAlign.Stretch; const verticalAlignTop = !ob.verticalAlign || ob.verticalAlign === VerticalAlign.TOP; if (verticalAlignTop && justOneItem) { - return extend({}, ob, { type: "bi.horizontal_auto" }); + return extend({}, ob, { type: HorizontalAutoLayout.xtype }); } const supportFlex = isSupportFlex(); // 在横向自适应场景下我们需要使用table的自适应撑出滚动条的特性(flex处理不了这种情况) @@ -164,26 +179,26 @@ configWidget("bi.horizontal_adapt", ob => { if (isAdapt) { return extend({ horizontalAlign: HorizontalAlign.Center, - }, ob, { type: "bi.table_adapt" }); + }, ob, { type: TableAdaptLayout.xtype }); } if (supportFlex) { return extend({ horizontalAlign: HorizontalAlign.Center, scrollx: false, - }, ob, { type: "bi.flex_horizontal" }); + }, ob, { type: FlexHorizontalLayout.xtype }); } return extend({ horizontalAlign: HorizontalAlign.Center, - }, ob, { type: "bi.table_adapt" }); + }, ob, { type: TableAdaptLayout.xtype }); }); configWidget("bi.horizontal_float", ob => { if (isSupportFlex()) { - return extend({}, ob, { type: "bi.flex_horizontal_adapt" }); + return extend({}, ob, { type: FlexHorizontalCenter.xtype }); } if (ob.items && ob.items.length <= 1) { - return extend({}, ob, { type: "bi.inline_horizontal_adapt" }); + return extend({}, ob, { type: InlineHorizontalAdaptLayout.xtype }); } return ob; @@ -195,17 +210,17 @@ configWidget("bi.horizontal_fill", ob => { horizontalAlign: HorizontalAlign.Stretch, verticalAlign: VerticalAlign.Stretch, scrollx: false, - }, ob, { type: "bi.flex_horizontal" }); + }, ob, { type: FlexHorizontalLayout.xtype }); } if ((ob.horizontalAlign && ob.horizontalAlign !== HorizontalAlign.Stretch) || (ob.scrollable === true || ob.scrollx === true)) { // 宽度不受限,要用table布局 return extend({ horizontalAlign: HorizontalAlign.Stretch, verticalAlign: VerticalAlign.Stretch, - }, ob, { type: "bi.table_adapt" }); + }, ob, { type: TableLayout.xtype }); } - return extend({}, ob, { type: "bi.horizontal_float_fill" }); + return extend({}, ob, { type: FloatHorizontalFillLayout.xtype }); }); configWidget("bi.vertical_fill", ob => { if (isSupportFlex()) { @@ -213,12 +228,12 @@ configWidget("bi.vertical_fill", ob => { horizontalAlign: HorizontalAlign.Stretch, verticalAlign: VerticalAlign.Stretch, scrolly: false, - }, ob, { type: "bi.flex_vertical" }); + }, ob, { type: FlexVerticalLayout }); } if (ob.scrollable === true || ob.scrollx === true || ob.scrolly === true) { // 有滚动条,降级到table布局处理 return extend({}, ob, { - type: "bi.td", + type: TdLayout.xtype, items: map(ob.items, (i, item) => [item]), }); } @@ -236,19 +251,19 @@ configWidget("bi.vertical_fill", ob => { } if (hasAuto) { // 有自动高的时候 - return extend({}, ob, { type: "bi.vtape_auto" }); + return extend({}, ob, { type: AutoVerticalTapeLayout.xtype }); } - return extend({}, ob, { type: "bi.vtape" }); + return extend({}, ob, { type: VTapeLayout.xtype }); }); configWidget("bi.horizontal_sticky", ob => { if (!isSupportSticky) { - return extend({ scrollx: true }, ob, { type: "bi.horizontal_fill" }); + return extend({ scrollx: true }, ob, { type: HorizontalFillLayout.xtype }); } }); configWidget("bi.vertical_sticky", ob => { if (!isSupportSticky) { - return extend({ scrolly: true }, ob, { type: "bi.vertical_fill" }); + return extend({ scrolly: true }, ob, { type: VerticalFillLayout.xtype }); } }); @@ -256,7 +271,7 @@ configWidget("bi.left_right_vertical_adapt", ob => { if (isSupportFlex()) { // IE下其实也是可以使用flex布局的,只要排除掉出现滚动条的情况 // if (!isIE() || (ob.scrollable !== true && ob.scrolly !== true)) { - return extend({}, ob, { type: "bi.flex_left_right_vertical_adapt" }); + return extend({}, ob, { type: FlexLeftRightVerticalAdaptLayout.xtype }); // } } @@ -265,28 +280,28 @@ configWidget("bi.left_right_vertical_adapt", ob => { configWidget("bi.flex_horizontal", ob => { if (ob.scrollable === true || ob.scrollx === true || ob.scrolly === true) { if (ob.hgap > 0 || ob.lgap > 0 || ob.rgap > 0) { - if (Providers.getProvider("bi.provider.system").getResponsiveMode()) { - return extend({}, ob, { type: "bi.responsive_flex_scrollable_horizontal" }); + if (Providers.getProvider(SystemProvider.xtype).getResponsiveMode()) { + return extend({}, ob, { type: ResponsiveFlexWrapperHorizontalLayout.xtype }); } - return extend({}, ob, { type: "bi.flex_scrollable_horizontal" }); + return extend({}, ob, { type: FlexWrapperHorizontalLayout.xtype }); } } - if (Providers.getProvider("bi.provider.system").getResponsiveMode()) { - return extend({}, ob, { type: "bi.responsive_flex_horizontal" }); + if (Providers.getProvider(SystemProvider.xtype).getResponsiveMode()) { + return extend({}, ob, { type: ResponsiveFlexHorizontalLayout.xtype }); } }); configWidget("bi.flex_vertical", ob => { if (ob.scrollable === true || ob.scrollx === true || ob.scrolly === true) { if (ob.hgap > 0 || ob.lgap > 0 || ob.rgap > 0) { - return extend({}, ob, { type: "bi.flex_scrollable_vertical" }); + return extend({}, ob, { type: FlexWrapperVerticalLayout.xtype }); } } }); configWidget("bi.table", ob => { if (!isSupportGrid()) { - return extend({}, ob, { type: "bi.td" }); + return extend({}, ob, { type: TdLayout.xtype }); } return ob; diff --git a/src/core/system.js b/src/core/system.js index dc66fd40b..9ec00cc69 100644 --- a/src/core/system.js +++ b/src/core/system.js @@ -4,7 +4,7 @@ * Created by windy on 2021/6/30 */ import { prepares } from "./0.foundation"; -import { deepExtend, extend, inherit, object } from "./2.base"; +import { deepExtend, extend } from "./2.base"; import { OB } from "./3.ob"; import { Providers } from "./5.inject"; import { provider } from "./decorator"; diff --git a/src/index.js b/src/index.js index 90bde9f4b..c4769cee1 100644 --- a/src/index.js +++ b/src/index.js @@ -1,8 +1,13 @@ import "./polyfill"; +import _jquery from "jquery"; + export * from "./core"; export * from "./base"; export * from "./case"; export * from "./widget"; export * from "./component"; - export * from "./fix"; +export * from "./router"; + +export const jQuery = _jquery; +export const $ = _jquery; diff --git a/src/router/0.router.js b/src/router/0.router.js index adf48b1bc..a14feefa7 100644 --- a/src/router/0.router.js +++ b/src/router/0.router.js @@ -1,627 +1,707 @@ -(function () { - var Events = { - - // Bind an event to a `callback` function. Passing `"all"` will bind - // the callback to all events fired. - on: function (name, callback, context) { - if (!eventsApi(this, "on", name, [callback, context]) || !callback) return this; - this._events || (this._events = {}); - var events = this._events[name] || (this._events[name] = []); - events.push({callback: callback, context: context, ctx: context || this}); - return this; - }, - - // Bind an event to only be triggered a single time. After the first time - // the callback is invoked, it will be removed. - once: function (name, callback, context) { - if (!eventsApi(this, "once", name, [callback, context]) || !callback) return this; - var self = this; - var once = BI._.once(function () { - self.off(name, once); - callback.apply(this, arguments); - }); - once._callback = callback; - return this.on(name, once, context); - }, - - // Remove one or many callbacks. If `context` is null, removes all - // callbacks with that function. If `callback` is null, removes all - // callbacks for the event. If `name` is null, removes all bound - // callbacks for all events. - off: function (name, callback, context) { - if (!this._events || !eventsApi(this, "off", name, [callback, context])) return this; - - // Remove all callbacks for all events. - if (!name && !callback && !context) { - this._events = void 0; - return this; - } +import { _ } from "@/core"; - var names = name ? [name] : BI._.keys(this._events); - for (var i = 0, length = names.length; i < length; i++) { - name = names[i]; - // Bail out if there are no events stored. - var events = this._events[name]; - if (!events) continue; +const Events = { + // Bind an event to a `callback` function. Passing `"all"` will bind + // the callback to all events fired. + on(name, callback, context) { + if (!eventsApi(this, "on", name, [callback, context]) || !callback) { + return this; + } + this._events || (this._events = {}); + const events = this._events[name] || (this._events[name] = []); + events.push({ + callback, + context, + ctx: context || this + }); + + return this; + }, + + // Bind an event to only be triggered a single time. After the first time + // the callback is invoked, it will be removed. + once(name, callback, context) { + if (!eventsApi(this, "once", name, [callback, context]) || !callback) { + return this; + } + const self = this; + var once = _.once(function () { + self.off(name, once); + callback.apply(this, arguments); + }); + once._callback = callback; + + return this.on(name, once, context); + }, + + // Remove one or many callbacks. If `context` is null, removes all + // callbacks with that function. If `callback` is null, removes all + // callbacks for the event. If `name` is null, removes all bound + // callbacks for all events. + off(name, callback, context) { + if ( + !this._events || + !eventsApi(this, "off", name, [callback, context]) + ) { + return this; + } - // Remove all callbacks for this event. - if (!callback && !context) { - delete this._events[name]; - continue; - } + // Remove all callbacks for all events. + if (!name && !callback && !context) { + this._events = void 0; - // Find any remaining events. - var remaining = []; - for (var j = 0, k = events.length; j < k; j++) { - var event = events[j]; - if ( - callback && callback !== event.callback && - callback !== event.callback._callback || - context && context !== event.context - ) { - remaining.push(event); - } - } + return this; + } - // Replace events if there are any remaining. Otherwise, clean up. - if (remaining.length) { - this._events[name] = remaining; - } else { - delete this._events[name]; - } - } + const names = name ? [name] : _.keys(this._events); + for (let i = 0, length = names.length; i < length; i++) { + name = names[i]; - return this; - }, - - un: function () { - this.off.apply(this, arguments); - }, - - // Trigger one or many events, firing all bound callbacks. Callbacks are - // passed the same arguments as `trigger` is, apart from the event name - // (unless you're listening on `"all"`, which will cause your callback to - // receive the true name of the event as the first argument). - trigger: function (name) { - if (!this._events) return this; - var args = slice.call(arguments, 1); - if (!eventsApi(this, "trigger", name, args)) return this; - var events = this._events[name]; - var allEvents = this._events.all; - if (events) triggerEvents(events, args); - if (allEvents) triggerEvents(allEvents, arguments); - return this; - }, - - fireEvent: function () { - this.trigger.apply(this, arguments); - }, - - // Inversion-of-control versions of `on` and `once`. Tell *this* object to - // listen to an event in another object ... keeping track of what it's - // listening to. - listenTo: function (obj, name, callback) { - var listeningTo = this._listeningTo || (this._listeningTo = {}); - var id = obj._listenId || (obj._listenId = BI._.uniqueId("l")); - listeningTo[id] = obj; - if (!callback && typeof name === "object") callback = this; - obj.on(name, callback, this); - return this; - }, + // Bail out if there are no events stored. + const events = this._events[name]; + if (!events) continue; - listenToOnce: function (obj, name, callback) { - if (typeof name === "object") { - for (var event in name) this.listenToOnce(obj, event, name[event]); - return this; + // Remove all callbacks for this event. + if (!callback && !context) { + delete this._events[name]; + continue; } - if (eventSplitter.test(name)) { - var names = name.split(eventSplitter); - for (var i = 0, length = names.length; i < length; i++) { - this.listenToOnce(obj, names[i], callback); + + // Find any remaining events. + const remaining = []; + for (let j = 0, k = events.length; j < k; j++) { + const event = events[j]; + if ( + (callback && + callback !== event.callback && + callback !== event.callback._callback) || + (context && context !== event.context) + ) { + remaining.push(event); } - return this; } - if (!callback) return this; - var once = BI._.once(function () { - this.stopListening(obj, name, once); - callback.apply(this, arguments); - }); - once._callback = callback; - return this.listenTo(obj, name, once); - }, - - // Tell this object to stop listening to either specific events ... or - // to every object it's currently listening to. - stopListening: function (obj, name, callback) { - var listeningTo = this._listeningTo; - if (!listeningTo) return this; - var remove = !name && !callback; - if (!callback && typeof name === "object") callback = this; - if (obj) (listeningTo = {})[obj._listenId] = obj; - for (var id in listeningTo) { - obj = listeningTo[id]; - obj.off(name, callback, this); - if (remove || BI._.isEmpty(obj._events)) delete this._listeningTo[id]; + + // Replace events if there are any remaining. Otherwise, clean up. + if (remaining.length) { + this._events[name] = remaining; + } else { + delete this._events[name]; } - return this; } - }; - - // Regular expression used to split event strings. - var eventSplitter = /\s+/; - - // Implement fancy features of the Events API such as multiple event - // names `"change blur"` and jQuery-style event maps `{change: action}` - // in terms of the existing API. - var eventsApi = function (obj, action, name, rest) { - if (!name) return true; - - // Handle event maps. + return this; + }, + + un() { + this.off.apply(this, arguments); + }, + + // Trigger one or many events, firing all bound callbacks. Callbacks are + // passed the same arguments as `trigger` is, apart from the event name + // (unless you're listening on `"all"`, which will cause your callback to + // receive the true name of the event as the first argument). + trigger(name) { + if (!this._events) return this; + const args = slice.call(arguments, 1); + if (!eventsApi(this, "trigger", name, args)) return this; + const events = this._events[name]; + const allEvents = this._events.all; + if (events) triggerEvents(events, args); + if (allEvents) triggerEvents(allEvents, arguments); + + return this; + }, + + fireEvent() { + this.trigger.apply(this, arguments); + }, + + // Inversion-of-control versions of `on` and `once`. Tell *this* object to + // listen to an event in another object ... keeping track of what it's + // listening to. + listenTo(obj, name, callback) { + const listeningTo = this._listeningTo || (this._listeningTo = {}); + const id = obj._listenId || (obj._listenId = _.uniqueId("l")); + listeningTo[id] = obj; + if (!callback && typeof name === "object") callback = this; + obj.on(name, callback, this); + + return this; + }, + + listenToOnce(obj, name, callback) { if (typeof name === "object") { - for (var key in name) { - obj[action].apply(obj, [key, name[key]].concat(rest)); + for (const event in name) { + this.listenToOnce(obj, event, name[event]); } - return false; - } - // Handle space separated event names. + return this; + } if (eventSplitter.test(name)) { - var names = name.split(eventSplitter); - for (var i = 0, length = names.length; i < length; i++) { - obj[action].apply(obj, [names[i]].concat(rest)); + const names = name.split(eventSplitter); + for (let i = 0, length = names.length; i < length; i++) { + this.listenToOnce(obj, names[i], callback); } - return false; - } - return true; - }; - - // A difficult-to-believe, but optimized internal dispatch function for - // triggering events. Tries to keep the usual cases speedy (most internal - // BI events have 3 arguments). - var triggerEvents = function (events, args) { - var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2]; - switch (args.length) { - case 0: - while (++i < l) (ev = events[i]).callback.call(ev.ctx); - return; - case 1: - while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); - return; - case 2: - while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); - return; - case 3: - while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); - return; - default: - while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); - return; + return this; } - }; - - // BI.Router - // --------------- - - // Routers map faux-URLs to actions, and fire events when routes are - // matched. Creating a new one sets its `routes` hash, if not set statically. - var Router = BI.Router = function (options) { - options || (options = {}); - if (options.routes) this.routes = options.routes; - this._bindRoutes(); - this._init.apply(this, arguments); - }; - - // Cached regular expressions for matching named param parts and splatted - // parts of route strings. - var optionalParam = /\((.*?)\)/g; - var namedParam = /(\(\?)?:\w+/g; - var splatParam = /\*\w+/g; - var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; - - // Set up all inheritable **BI.Router** properties and methods. - BI._.extend(Router.prototype, Events, { - - // _init is an empty function by default. Override it with your own - // initialization logic. - _init: function () { - }, - - // Manually bind a single named route to a callback. For example: - // - // this.route('search/:query/p:num', 'search', function(query, num) { - // ... - // }); - // - route: function (route, name, callback) { - if (!BI._.isRegExp(route)) route = this._routeToRegExp(route); - if (BI._.isFunction(name)) { - callback = name; - name = ""; + if (!callback) return this; + var once = _.once(function () { + this.stopListening(obj, name, once); + callback.apply(this, arguments); + }); + once._callback = callback; + + return this.listenTo(obj, name, once); + }, + + // Tell this object to stop listening to either specific events ... or + // to every object it's currently listening to. + stopListening(obj, name, callback) { + let listeningTo = this._listeningTo; + if (!listeningTo) return this; + const remove = !name && !callback; + if (!callback && typeof name === "object") callback = this; + if (obj) (listeningTo = {})[obj._listenId] = obj; + for (const id in listeningTo) { + obj = listeningTo[id]; + obj.off(name, callback, this); + if (remove || _.isEmpty(obj._events)) { + delete this._listeningTo[id]; } - if (!callback) callback = this[name]; - var router = this; - BI.history.route(route, function (fragment) { - var args = router._extractParameters(route, fragment); - if (router.execute(callback, args, name) !== false) { - router.trigger.apply(router, ["route:" + name].concat(args)); - router.trigger("route", name, args); - BI.history.trigger("route", router, name, args); - } - }); - return this; - }, + } - // Execute a route handler with the provided parameters. This is an - // excellent place to do pre-route setup or post-route cleanup. - execute: function (callback, args, name) { - if (callback) callback.apply(this, args); - }, + return this; + } +}; - // Simple proxy to `BI.history` to save a fragment into the history. - navigate: function (fragment, options) { - BI.history.navigate(fragment, options); - return this; - }, - - // Bind all defined routes to `BI.history`. We have to reverse the - // order of the routes here to support behavior where the most general - // routes can be defined at the bottom of the route map. - _bindRoutes: function () { - if (!this.routes) return; - this.routes = BI._.result(this, "routes"); - var route, routes = BI._.keys(this.routes); - while ((route = routes.pop()) != null) { - this.route(route, this.routes[route]); - } - }, - - // Convert a route string into a regular expression, suitable for matching - // against the current location hash. - _routeToRegExp: function (route) { - route = route.replace(escapeRegExp, "\\$&") - .replace(optionalParam, "(?:$1)?") - .replace(namedParam, function (match, optional) { - return optional ? match : "([^/?]+)"; - }) - .replace(splatParam, "([^?]*?)"); - return new RegExp("^" + route + "(?:\\?([\\s\\S]*))?$"); - }, - - // Given a route, and a URL fragment that it matches, return the array of - // extracted decoded parameters. Empty or unmatched parameters will be - // treated as `null` to normalize cross-browser behavior. - _extractParameters: function (route, fragment) { - var params = route.exec(fragment).slice(1); - return BI._.map(params, function (param, i) { - // Don't decode the search params. - if (i === params.length - 1) return param || null; - var resultParam = null; - if (param) { - try { - resultParam = decodeURIComponent(param); - } catch (e) { - resultParam = param; - } - } - return resultParam; - }); +// Regular expression used to split event strings. +var eventSplitter = /\s+/; + +// Implement fancy features of the Events API such as multiple event +// names `"change blur"` and jQuery-style event maps `{change: action}` +// in terms of the existing API. +var eventsApi = function (obj, action, name, rest) { + if (!name) return true; + + // Handle event maps. + if (typeof name === "object") { + for (const key in name) { + obj[action].apply(obj, [key, name[key]].concat(rest)); } - }); + return false; + } - // History - // ---------------- + // Handle space separated event names. + if (eventSplitter.test(name)) { + const names = name.split(eventSplitter); + for (let i = 0, length = names.length; i < length; i++) { + obj[action].apply(obj, [names[i]].concat(rest)); + } - // Handles cross-browser history management, based on either - // [pushState](http://diveintohtml5.info/history.html) and real URLs, or - // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange) - // and URL fragments. If the browser supports neither (old IE, natch), - // falls back to polling. - var History = function () { - this.handlers = []; - this.checkUrl = BI._.bind(this.checkUrl, this); + return false; + } + + return true; +}; + +// A difficult-to-believe, but optimized internal dispatch function for +// triggering events. Tries to keep the usual cases speedy (most internal +// BI events have 3 arguments). +var triggerEvents = function (events, args) { + let ev, + i = -1, + l = events.length, + a1 = args[0], + a2 = args[1], + a3 = args[2]; + switch (args.length) { + case 0: + while (++i < l) (ev = events[i]).callback.call(ev.ctx); + + return; + case 1: + while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); + + return; + case 2: + while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); + + return; + case 3: + while (++i < l) { + (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); + } - // Ensure that `History` can be used outside of the browser. - if (typeof window !== "undefined") { - this.location = _global.location; - this.history = _global.history; + return; + default: + while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); + + return; + } +}; + +// BI.Router +// --------------- + +// Routers map faux-URLs to actions, and fire events when routes are +// matched. Creating a new one sets its `routes` hash, if not set statically. +export const Router = function (options) { + options || (options = {}); + if (options.routes) this.routes = options.routes; + this._bindRoutes(); + this._init.apply(this, arguments); +}; + +// Cached regular expressions for matching named param parts and splatted +// parts of route strings. +const optionalParam = /\((.*?)\)/g; +const namedParam = /(\(\?)?:\w+/g; +const splatParam = /\*\w+/g; +const escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; + +// Set up all inheritable **BI.Router** properties and methods. +_.extend(Router.prototype, Events, { + // _init is an empty function by default. Override it with your own + // initialization logic. + _init() {}, + + // Manually bind a single named route to a callback. For example: + // + // this.route('search/:query/p:num', 'search', function(query, num) { + // ... + // }); + // + route(route, name, callback) { + if (!_.isRegExp(route)) route = this._routeToRegExp(route); + if (_.isFunction(name)) { + callback = name; + name = ""; } - }; - - // Cached regex for stripping a leading hash/slash and trailing space. - var routeStripper = /^[#\/]|\s+$/g; - - // Cached regex for stripping leading and trailing slashes. - var rootStripper = /^\/+|\/+$/g; - - // Cached regex for stripping urls of hash. - var pathStripper = /#.*$/; - - // Has the history handling already been started? - History.started = false; - - // Set up all inheritable **BI.History** properties and methods. - BI._.extend(History.prototype, Events, { - - // The default interval to poll for hash changes, if necessary, is - // twenty times a second. - interval: 50, - - // Are we at the app root? - atRoot: function () { - var path = this.location.pathname.replace(/[^\/]$/, "$&/"); - return path === this.root && !this.getSearch(); - }, - - // In IE6, the hash fragment and search params are incorrect if the - // fragment contains `?`. - getSearch: function () { - var match = this.location.href.replace(/#.*/, "").match(/\?.+/); - return match ? match[0] : ""; - }, - - // Gets the true hash value. Cannot use location.hash directly due to bug - // in Firefox where location.hash will always be decoded. - getHash: function (window) { - var match = (window || this).location.href.match(/#(.*)$/); - return match ? match[1] : ""; - }, - - // Get the pathname and search params, without the root. - getPath: function () { - var path = this.location.pathname + this.getSearch(); - try { - path = decodeURI(path); - } catch(e) { + if (!callback) callback = this[name]; + const router = this; + history.route(route, (fragment) => { + const args = router._extractParameters(route, fragment); + if (router.execute(callback, args, name) !== false) { + router.trigger.apply(router, [`route:${name}`].concat(args)); + router.trigger("route", name, args); + history.trigger("route", router, name, args); } - var root = this.root.slice(0, -1); - if (!path.indexOf(root)) path = path.slice(root.length); - return path.charAt(0) === "/" ? path.slice(1) : path; - }, - - // Get the cross-browser normalized URL fragment from the path or hash. - getFragment: function (fragment) { - if (fragment == null) { - if (this._hasPushState || !this._wantsHashChange) { - fragment = this.getPath(); - } else { - fragment = this.getHash(); + }); + + return this; + }, + + // Execute a route handler with the provided parameters. This is an + // excellent place to do pre-route setup or post-route cleanup. + execute(callback, args, name) { + if (callback) callback.apply(this, args); + }, + + // Simple proxy to `BI.history` to save a fragment into the history. + navigate(fragment, options) { + history.navigate(fragment, options); + + return this; + }, + + // Bind all defined routes to `BI.history`. We have to reverse the + // order of the routes here to support behavior where the most general + // routes can be defined at the bottom of the route map. + _bindRoutes() { + if (!this.routes) return; + this.routes = _.result(this, "routes"); + let route, + routes = _.keys(this.routes); + while ((route = routes.pop()) != null) { + this.route(route, this.routes[route]); + } + }, + + // Convert a route string into a regular expression, suitable for matching + // against the current location hash. + _routeToRegExp(route) { + route = route + .replace(escapeRegExp, "\\$&") + .replace(optionalParam, "(?:$1)?") + .replace(namedParam, (match, optional) => + optional ? match : "([^/?]+)" + ) + .replace(splatParam, "([^?]*?)"); + + return new RegExp(`^${route}(?:\\?([\\s\\S]*))?$`); + }, + + // Given a route, and a URL fragment that it matches, return the array of + // extracted decoded parameters. Empty or unmatched parameters will be + // treated as `null` to normalize cross-browser behavior. + _extractParameters(route, fragment) { + const params = route.exec(fragment).slice(1); + + return _.map(params, (param, i) => { + // Don't decode the search params. + if (i === params.length - 1) return param || null; + let resultParam = null; + if (param) { + try { + resultParam = decodeURIComponent(param); + } catch (e) { + resultParam = param; } } - return fragment.replace(routeStripper, ""); - }, - - // Start the hash change handling, returning `true` if the current URL matches - // an existing route, and `false` otherwise. - start: function (options) { - if (History.started) throw new Error("BI.history has already been started"); - History.started = true; - - // Figure out the initial configuration. Do we need an iframe? - // Is pushState desired ... is it available? - this.options = BI._.extend({root: "/"}, this.options, options); - this.root = this.options.root; - this._wantsHashChange = this.options.hashChange !== false; - this._hasHashChange = "onhashchange" in window; - this._wantsPushState = !!this.options.pushState; - this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState); - this.fragment = this.getFragment(); - - // Normalize root to always include a leading and trailing slash. - this.root = ("/" + this.root + "/").replace(rootStripper, "/"); - - // Transition from hashChange to pushState or vice versa if both are - // requested. - if (this._wantsHashChange && this._wantsPushState) { - - // If we've started off with a route from a `pushState`-enabled - // browser, but we're currently in a browser that doesn't support it... - if (!this._hasPushState && !this.atRoot()) { - var root = this.root.slice(0, -1) || "/"; - this.location.replace(root + "#" + this.getPath()); - // Return immediately as browser will do redirect to new url - return true; - - // Or if we've started out with a hash-based route, but we're currently - // in a browser where it could be `pushState`-based instead... - } else if (this._hasPushState && this.atRoot()) { - this.navigate(this.getHash(), {replace: true}); - } + return resultParam; + }); + } +}); + +// History +// ---------------- + +// Handles cross-browser history management, based on either +// [pushState](http://diveintohtml5.info/history.html) and real URLs, or +// [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange) +// and URL fragments. If the browser supports neither (old IE, natch), +// falls back to polling. +const History = function () { + this.handlers = []; + this.checkUrl = _.bind(this.checkUrl, this); + + // Ensure that `History` can be used outside of the browser. + if (typeof window !== "undefined") { + this.location = _global.location; + this.history = _global.history; + } +}; + +// Cached regex for stripping a leading hash/slash and trailing space. +const routeStripper = /^[#\/]|\s+$/g; + +// Cached regex for stripping leading and trailing slashes. +const rootStripper = /^\/+|\/+$/g; + +// Cached regex for stripping urls of hash. +const pathStripper = /#.*$/; + +// Has the history handling already been started? +History.started = false; + +// Set up all inheritable **BI.History** properties and methods. +_.extend(History.prototype, Events, { + // The default interval to poll for hash changes, if necessary, is + // twenty times a second. + interval: 50, + + // Are we at the app root? + atRoot() { + const path = this.location.pathname.replace(/[^\/]$/, "$&/"); + + return path === this.root && !this.getSearch(); + }, + + // In IE6, the hash fragment and search params are incorrect if the + // fragment contains `?`. + getSearch() { + const match = this.location.href.replace(/#.*/, "").match(/\?.+/); + + return match ? match[0] : ""; + }, + + // Gets the true hash value. Cannot use location.hash directly due to bug + // in Firefox where location.hash will always be decoded. + getHash(window) { + const match = (window || this).location.href.match(/#(.*)$/); + + return match ? match[1] : ""; + }, + + // Get the pathname and search params, without the root. + getPath() { + let path = this.location.pathname + this.getSearch(); + try { + path = decodeURI(path); + } catch (e) {} + const root = this.root.slice(0, -1); + if (!path.indexOf(root)) path = path.slice(root.length); + + return path.charAt(0) === "/" ? path.slice(1) : path; + }, + + // Get the cross-browser normalized URL fragment from the path or hash. + getFragment(fragment) { + if (fragment == null) { + if (this._hasPushState || !this._wantsHashChange) { + fragment = this.getPath(); + } else { + fragment = this.getHash(); } + } - // Proxy an iframe to handle location events if the browser doesn't - // support the `hashchange` event, HTML5 history, or the user wants - // `hashChange` but not `pushState`. - if (!this._hasHashChange && this._wantsHashChange && (!this._wantsPushState || !this._hasPushState)) { - var iframe = document.createElement("iframe"); - iframe.src = "javascript:0"; - iframe.style.display = "none"; - iframe.tabIndex = -1; - var body = document.body; - // Using `appendChild` will throw on IE < 9 if the document is not ready. - this.iframe = body.insertBefore(iframe, body.firstChild).contentWindow; - this.iframe.document.open().close(); - this.iframe.location.hash = "#" + this.fragment; + return fragment.replace(routeStripper, ""); + }, + + // Start the hash change handling, returning `true` if the current URL matches + // an existing route, and `false` otherwise. + start(options) { + if (History.started) { + throw new Error("BI.history has already been started"); + } + History.started = true; + + // Figure out the initial configuration. Do we need an iframe? + // Is pushState desired ... is it available? + this.options = _.extend({ root: "/" }, this.options, options); + this.root = this.options.root; + this._wantsHashChange = this.options.hashChange !== false; + this._hasHashChange = "onhashchange" in window; + this._wantsPushState = !!this.options.pushState; + this._hasPushState = !!( + this.options.pushState && + this.history && + this.history.pushState + ); + this.fragment = this.getFragment(); + + // Normalize root to always include a leading and trailing slash. + this.root = `/${this.root}/`.replace(rootStripper, "/"); + + // Transition from hashChange to pushState or vice versa if both are + // requested. + if (this._wantsHashChange && this._wantsPushState) { + // If we've started off with a route from a `pushState`-enabled + // browser, but we're currently in a browser that doesn't support it... + if (!this._hasPushState && !this.atRoot()) { + const root = this.root.slice(0, -1) || "/"; + this.location.replace(`${root}#${this.getPath()}`); + // Return immediately as browser will do redirect to new url + return true; + + // Or if we've started out with a hash-based route, but we're currently + // in a browser where it could be `pushState`-based instead... + } else if (this._hasPushState && this.atRoot()) { + this.navigate(this.getHash(), { replace: true }); } + } - // Add a cross-platform `addEventListener` shim for older browsers. - var addEventListener = _global.addEventListener || function (eventName, listener) { - return attachEvent("on" + eventName, listener); - }; + // Proxy an iframe to handle location events if the browser doesn't + // support the `hashchange` event, HTML5 history, or the user wants + // `hashChange` but not `pushState`. + if ( + !this._hasHashChange && + this._wantsHashChange && + (!this._wantsPushState || !this._hasPushState) + ) { + const iframe = document.createElement("iframe"); + iframe.src = "javascript:0"; + iframe.style.display = "none"; + iframe.tabIndex = -1; + const body = document.body; + // Using `appendChild` will throw on IE < 9 if the document is not ready. + this.iframe = body.insertBefore( + iframe, + body.firstChild + ).contentWindow; + this.iframe.document.open().close(); + this.iframe.location.hash = `#${this.fragment}`; + } - // Depending on whether we're using pushState or hashes, and whether - // 'onhashchange' is supported, determine how we check the URL state. - if (this._hasPushState) { - addEventListener("popstate", this.checkUrl, false); - } else if (this._wantsHashChange && this._hasHashChange && !this.iframe) { - addEventListener("hashchange", this.checkUrl, false); - } else if (this._wantsHashChange) { - this._checkUrlInterval = setInterval(this.checkUrl, this.interval); - } + // Add a cross-platform `addEventListener` shim for older browsers. + const addEventListener = + _global.addEventListener || + function (eventName, listener) { + return attachEvent(`on${eventName}`, listener); + }; - if (!this.options.silent) return this.loadUrl(); - }, + // Depending on whether we're using pushState or hashes, and whether + // 'onhashchange' is supported, determine how we check the URL state. + if (this._hasPushState) { + addEventListener("popstate", this.checkUrl, false); + } else if ( + this._wantsHashChange && + this._hasHashChange && + !this.iframe + ) { + addEventListener("hashchange", this.checkUrl, false); + } else if (this._wantsHashChange) { + this._checkUrlInterval = setInterval(this.checkUrl, this.interval); + } - // Disable BI.history, perhaps temporarily. Not useful in a real app, - // but possibly useful for unit testing Routers. - stop: function () { - // Add a cross-platform `removeEventListener` shim for older browsers. - var removeEventListener = _global.removeEventListener || function (eventName, listener) { - return detachEvent("on" + eventName, listener); + if (!this.options.silent) return this.loadUrl(); + }, + + // Disable BI.history, perhaps temporarily. Not useful in a real app, + // but possibly useful for unit testing Routers. + stop() { + // Add a cross-platform `removeEventListener` shim for older browsers. + const removeEventListener = + _global.removeEventListener || + function (eventName, listener) { + return detachEvent(`on${eventName}`, listener); }; - // Remove window listeners. - if (this._hasPushState) { - removeEventListener("popstate", this.checkUrl, false); - } else if (this._wantsHashChange && this._hasHashChange && !this.iframe) { - removeEventListener("hashchange", this.checkUrl, false); - } + // Remove window listeners. + if (this._hasPushState) { + removeEventListener("popstate", this.checkUrl, false); + } else if ( + this._wantsHashChange && + this._hasHashChange && + !this.iframe + ) { + removeEventListener("hashchange", this.checkUrl, false); + } - // Clean up the iframe if necessary. - if (this.iframe) { - document.body.removeChild(this.iframe.frameElement); - this.iframe = null; - } + // Clean up the iframe if necessary. + if (this.iframe) { + document.body.removeChild(this.iframe.frameElement); + this.iframe = null; + } - // Some environments will throw when clearing an undefined interval. - if (this._checkUrlInterval) clearInterval(this._checkUrlInterval); - History.started = false; - }, - - // Add a route to be tested when the fragment changes. Routes added later - // may override previous routes. - route: function (route, callback) { - this.handlers.unshift({route: route, callback: callback}); - }, - - // check route is Exist. if exist, return the route - checkRoute: function (route) { - for (var i = 0; i < this.handlers.length; i++) { - if (this.handlers[i].route.toString() === Router.prototype._routeToRegExp(route).toString()) { - return this.handlers[i]; - } + // Some environments will throw when clearing an undefined interval. + if (this._checkUrlInterval) clearInterval(this._checkUrlInterval); + History.started = false; + }, + + // Add a route to be tested when the fragment changes. Routes added later + // may override previous routes. + route(route, callback) { + this.handlers.unshift({ route, callback }); + }, + + // check route is Exist. if exist, return the route + checkRoute(route) { + for (let i = 0; i < this.handlers.length; i++) { + if ( + this.handlers[i].route.toString() === + Router.prototype._routeToRegExp(route).toString() + ) { + return this.handlers[i]; } + } - return null; - }, + return null; + }, - // remove a route match in routes - unRoute: function (route) { - var index = BI._.findIndex(this.handlers, function (handler) { - return handler.route.test(route); - }); - if (index > -1) { - this.handlers.splice(index, 1); - } - }, - - // Checks the current URL to see if it has changed, and if it has, - // calls `loadUrl`, normalizing across the hidden iframe. - checkUrl: function (e) { - var current = this.getFragment(); - try { - // getFragment 得到的值是编码过的,而this.fragment是没有编码过的 - // 英文路径没有问题,遇上中文和空格有问题了 - current = decodeURIComponent(current); - } catch(e) { - } - // If the user pressed the back button, the iframe's hash will have - // changed and we should use that for comparison. - if (current === this.fragment && this.iframe) { - current = this.getHash(this.iframe); - } + // remove a route match in routes + unRoute(route) { + const index = _.findIndex(this.handlers, (handler) => + handler.route.test(route) + ); + if (index > -1) { + this.handlers.splice(index, 1); + } + }, + + // Checks the current URL to see if it has changed, and if it has, + // calls `loadUrl`, normalizing across the hidden iframe. + checkUrl(e) { + let current = this.getFragment(); + try { + // getFragment 得到的值是编码过的,而this.fragment是没有编码过的 + // 英文路径没有问题,遇上中文和空格有问题了 + current = decodeURIComponent(current); + } catch (e) {} + // If the user pressed the back button, the iframe's hash will have + // changed and we should use that for comparison. + if (current === this.fragment && this.iframe) { + current = this.getHash(this.iframe); + } - if (current === this.fragment) return false; - if (this.iframe) this.navigate(current); - this.loadUrl(); - }, - - // Attempt to load the current URL fragment. If a route succeeds with a - // match, returns `true`. If no defined routes matches the fragment, - // returns `false`. - loadUrl: function (fragment) { - fragment = this.fragment = this.getFragment(fragment); - return BI._.some(this.handlers, function (handler) { - if (handler.route.test(fragment)) { - handler.callback(fragment); - return true; - } - }); - }, - - // Save a fragment into the hash history, or replace the URL state if the - // 'replace' option is passed. You are responsible for properly URL-encoding - // the fragment in advance. - // - // The options object can contain `trigger: true` if you wish to have the - // route callback be fired (not usually desirable), or `replace: true`, if - // you wish to modify the current URL without adding an entry to the history. - navigate: function (fragment, options) { - if (!History.started) return false; - if (!options || options === true) options = {trigger: !!options}; - - // Normalize the fragment. - fragment = this.getFragment(fragment || ""); - - // Don't include a trailing slash on the root. - var root = this.root; - if (fragment === "" || fragment.charAt(0) === "?") { - root = root.slice(0, -1) || "/"; - } - var url = root + fragment; + if (current === this.fragment) return false; + if (this.iframe) this.navigate(current); + this.loadUrl(); + }, - // Strip the hash and decode for matching. - fragment = fragment.replace(pathStripper, "") - try { - fragment = decodeURI(fragment); - } catch(e) { - } + // Attempt to load the current URL fragment. If a route succeeds with a + // match, returns `true`. If no defined routes matches the fragment, + // returns `false`. + loadUrl(fragment) { + fragment = this.fragment = this.getFragment(fragment); - if (this.fragment === fragment) return; - this.fragment = fragment; - - // If pushState is available, we use it to set the fragment as a real URL. - if (this._hasPushState) { - this.history[options.replace ? "replaceState" : "pushState"]({}, document.title, url); - - // If hash changes haven't been explicitly disabled, update the hash - // fragment to store history. - } else if (this._wantsHashChange) { - this._updateHash(this.location, fragment, options.replace); - if (this.iframe && (fragment !== this.getHash(this.iframe))) { - // Opening and closing the iframe tricks IE7 and earlier to push a - // history entry on hash-tag change. When replace is true, we don't - // want this. - if (!options.replace) this.iframe.document.open().close(); - this._updateHash(this.iframe.location, fragment, options.replace); - } + return _.some(this.handlers, (handler) => { + if (handler.route.test(fragment)) { + handler.callback(fragment); - // If you've told us that you explicitly don't want fallback hashchange- - // based history, then `navigate` becomes a page refresh. - } else { - return this.location.assign(url); - } - if (options.trigger) return this.loadUrl(fragment); - }, - - // Update the hash location, either replacing the current entry, or adding - // a new one to the browser history. - _updateHash: function (location, fragment, replace) { - if (replace) { - var href = location.href.replace(/(javascript:|#).*$/, ""); - location.replace(href + "#" + fragment); - } else { - // Some browsers require that `hash` contains a leading #. - location.hash = "#" + fragment; + return true; } + }); + }, + + // Save a fragment into the hash history, or replace the URL state if the + // 'replace' option is passed. You are responsible for properly URL-encoding + // the fragment in advance. + // + // The options object can contain `trigger: true` if you wish to have the + // route callback be fired (not usually desirable), or `replace: true`, if + // you wish to modify the current URL without adding an entry to the history. + navigate(fragment, options) { + if (!History.started) return false; + if (!options || options === true) options = { trigger: !!options }; + + // Normalize the fragment. + fragment = this.getFragment(fragment || ""); + + // Don't include a trailing slash on the root. + let root = this.root; + if (fragment === "" || fragment.charAt(0) === "?") { + root = root.slice(0, -1) || "/"; } + const url = root + fragment; + + // Strip the hash and decode for matching. + fragment = fragment.replace(pathStripper, ""); + try { + fragment = decodeURI(fragment); + } catch (e) {} + + if (this.fragment === fragment) return; + this.fragment = fragment; + + // If pushState is available, we use it to set the fragment as a real URL. + if (this._hasPushState) { + this.history[options.replace ? "replaceState" : "pushState"]( + {}, + document.title, + url + ); + + // If hash changes haven't been explicitly disabled, update the hash + // fragment to store history. + } else if (this._wantsHashChange) { + this._updateHash(this.location, fragment, options.replace); + if (this.iframe && fragment !== this.getHash(this.iframe)) { + // Opening and closing the iframe tricks IE7 and earlier to push a + // history entry on hash-tag change. When replace is true, we don't + // want this. + if (!options.replace) this.iframe.document.open().close(); + this._updateHash( + this.iframe.location, + fragment, + options.replace + ); + } - }); + // If you've told us that you explicitly don't want fallback hashchange- + // based history, then `navigate` becomes a page refresh. + } else { + return this.location.assign(url); + } + if (options.trigger) return this.loadUrl(fragment); + }, + + // Update the hash location, either replacing the current entry, or adding + // a new one to the browser history. + _updateHash(location, fragment, replace) { + if (replace) { + const href = location.href.replace(/(javascript:|#).*$/, ""); + location.replace(`${href}#${fragment}`); + } else { + // Some browsers require that `hash` contains a leading #. + location.hash = `#${fragment}`; + } + } +}); - // Create the default BI.history. - BI.history = new History; -}()); \ No newline at end of file +// Create the default BI.history. +export const history = new History(); \ No newline at end of file diff --git a/src/router/index.js b/src/router/index.js index dd7acd4c5..9e81827ef 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -1,2 +1,2 @@ export * from "./0.router"; -export * from "./router"; \ No newline at end of file +export * from "./router"; diff --git a/src/router/router.js b/src/router/router.js index 4a2cf0c2f..c91d34a4e 100644 --- a/src/router/router.js +++ b/src/router/router.js @@ -1,2343 +1,2464 @@ -/*! - * vue-router v3.5.2 - * (c) 2021 Evan You - * @license MIT - */ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define(factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory()); - }(this, (function () { 'use strict'; - - /* */ - - 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)); - } - } - - function extend (a, b) { - for (var key in b) { +import { nextTick, shortcut, Widget } from "@/core"; +import { Router } from "./0.router"; + +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}`); + } +} + +function extend(a, b) { + for (const key in b) { a[key] = b[key]; - } - return a - } - - /* */ - - var encodeReserveRE = /[!'()*]/g; - var encodeReserveReplacer = function (c) { return '%' + c.charCodeAt(0).toString(16); }; - var commaRE = /%2C/g; - - // fixed encodeURIComponent which is more conformant to RFC3986: - // - escapes [!'()*] - // - preserve commas - var encode = function (str) { return encodeURIComponent(str) + } + + return a; +} + +/* */ + +const encodeReserveRE = /[!'()*]/g; +const encodeReserveReplacer = function (c) { + return `%${c.charCodeAt(0).toString(16)}`; +}; +const commaRE = /%2C/g; + +// fixed encodeURIComponent which is more conformant to RFC3986: +// - escapes [!'()*] +// - preserve commas +const encode = function (str) { + return encodeURIComponent(str) .replace(encodeReserveRE, encodeReserveReplacer) - .replace(commaRE, ','); }; - - function decode (str) { - try { - return decodeURIComponent(str) - } catch (err) { + .replace(commaRE, ","); +}; + +function decode(str) { + try { + return decodeURIComponent(str); + } catch (err) { { - warn(false, ("Error decoding \"" + str + "\". Leaving it intact.")); + warn(false, `Error decoding "${str}". Leaving it intact.`); } - } - return str } - - function resolveQuery ( - query, - extraQuery, - _parseQuery - ) { - if ( extraQuery === void 0 ) extraQuery = {}; - - var parse = _parseQuery || parseQuery; - var parsedQuery; - try { - parsedQuery = parse(query || ''); - } catch (e) { + + return str; +} + +function resolveQuery(query, extraQuery, _parseQuery) { + if (extraQuery === void 0) extraQuery = {}; + + const parse = _parseQuery || parseQuery; + let parsedQuery; + try { + parsedQuery = parse(query || ""); + } catch (e) { warn(false, e.message); parsedQuery = {}; - } - for (var key in extraQuery) { - var value = extraQuery[key]; + } + for (const key in extraQuery) { + const value = extraQuery[key]; parsedQuery[key] = Array.isArray(value) - ? value.map(castQueryParamValue) - : castQueryParamValue(value); - } - return parsedQuery - } - - var castQueryParamValue = function (value) { return (value == null || typeof value === 'object' ? value : String(value)); }; - - 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; - + ? value.map(castQueryParamValue) + : castQueryParamValue(value); + } + + return parsedQuery; +} + +var castQueryParamValue = function (value) { + return value == null || typeof value === "object" ? value : String(value); +}; + +function parseQuery(query) { + const res = {}; + + query = query.trim().replace(/^(\?|#|&)/, ""); + + if (!query) { + return res; + } + + query.split("&").forEach((param) => { + const parts = param.replace(/\+/g, " ").split("="); + const key = decode(parts.shift()); + const val = parts.length > 0 ? decode(parts.join("=")) : null; + if (res[key] === undefined) { - res[key] = val; + res[key] = val; } else if (Array.isArray(res[key])) { - res[key].push(val); + res[key].push(val); } else { - res[key] = [res[key], val]; + res[key] = [res[key], val]; } - }); - - return res - } - - function stringifyQuery (obj) { - var res = obj + }); + + return res; +} + +function stringifyQuery(obj) { + const 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.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('&') + .map((key) => { + const val = obj[key]; + + if (val === undefined) { + return ""; + } + + if (val === null) { + return encode(key); + } + + if (Array.isArray(val)) { + const result = []; + val.forEach((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((x) => x.length > 0) + .join("&") : null; - return res ? ("?" + res) : '' - } - - /* */ - - var trailingSlashRE = /\/?$/; - - function createRoute ( - record, - location, - redirectedFrom, - router - ) { - var stringifyQuery = router && router.options.stringifyQuery; - - var query = location.query || {}; - try { + + return res ? `?${res}` : ""; +} + +/* */ + +const trailingSlashRE = /\/?$/; + +function createRoute(record, location, redirectedFrom, router) { + const stringifyQuery = router && router.options.stringifyQuery; + + let query = location.query || {}; + try { query = clone(query); - } catch (e) {} - - var route = { + } catch (e) {} + + const route = { name: location.name || (record && record.name), meta: (record && record.meta) || {}, - path: location.path || '/', - hash: location.hash || '', - query: query, + path: location.path || "/", + hash: location.hash || "", + query, params: location.params || {}, fullPath: getFullPath(location, stringifyQuery), matched: record ? formatMatch(record) : [] - }; - if (redirectedFrom) { + }; + if (redirectedFrom) { route.redirectedFrom = getFullPath(redirectedFrom, stringifyQuery); - } - return Object.freeze(route) - } - - function clone (value) { - if (Array.isArray(value)) { - return value.map(clone) - } else if (value && typeof value === 'object') { - var res = {}; - for (var key in value) { - res[key] = clone(value[key]); - } - return res - } else { - return value - } - } - - // the starting route that represents the initial state - var START = createRoute(null, { - path: '/' - }); - - function formatMatch (record) { - var res = []; - while (record) { + } + + return Object.freeze(route); +} + +function clone(value) { + if (Array.isArray(value)) { + return value.map(clone); + } else if (value && typeof value === "object") { + const res = {}; + for (const key in value) { + res[key] = clone(value[key]); + } + + return res; + } else { + return value; + } +} + +// the starting route that represents the initial state +const START = createRoute(null, { + path: "/" +}); + +function formatMatch(record) { + const res = []; + while (record) { res.unshift(record); record = record.parent; - } - return res } - - function getFullPath ( - ref, - _stringifyQuery - ) { - var path = ref.path; - var query = ref.query; if ( query === void 0 ) query = {}; - var hash = ref.hash; if ( hash === void 0 ) hash = ''; - - var stringify = _stringifyQuery || stringifyQuery; - return (path || '/') + stringify(query) + hash - } - - function isSameRoute (a, b, onlyPath) { - 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, '') && (onlyPath || - a.hash === b.hash && - isObjectEqual(a.query, b.query)) - } else if (a.name && b.name) { + + return res; +} + +function getFullPath(ref, _stringifyQuery) { + const path = ref.path; + let query = ref.query; + if (query === void 0) query = {}; + let hash = ref.hash; + if (hash === void 0) hash = ""; + + const stringify = _stringifyQuery || stringifyQuery; + + return (path || "/") + stringify(query) + hash; +} + +function isSameRoute(a, b, onlyPath) { + if (b === START) { + return a === b; + } else if (!b) { + return false; + } else if (a.path && b.path) { return ( - a.name === b.name && - (onlyPath || ( - 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 = {}; - - // handle null value #1566 - if (!a || !b) { return a === b } - var aKeys = Object.keys(a).sort(); - var bKeys = Object.keys(b).sort(); - if (aKeys.length !== bKeys.length) { - return false - } - return aKeys.every(function (key, i) { - var aVal = a[key]; - var bKey = bKeys[i]; - if (bKey !== key) { return false } - var bVal = b[key]; + a.path.replace(trailingSlashRE, "") === + b.path.replace(trailingSlashRE, "") && + (onlyPath || (a.hash === b.hash && isObjectEqual(a.query, b.query))) + ); + } else if (a.name && b.name) { + return ( + a.name === b.name && + (onlyPath || + (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 = {}; + + // handle null value #1566 + if (!a || !b) { + return a === b; + } + const aKeys = Object.keys(a).sort(); + const bKeys = Object.keys(b).sort(); + if (aKeys.length !== bKeys.length) { + return false; + } + + return aKeys.every((key, i) => { + const aVal = a[key]; + const bKey = bKeys[i]; + if (bKey !== key) { + return false; + } + const bVal = b[key]; // query values can be null and undefined - if (aVal == null || bVal == null) { return aVal === bVal } + if (aVal == null || bVal == null) { + return aVal === bVal; + } // check nested equality - if (typeof aVal === 'object' && typeof bVal === 'object') { - return isObjectEqual(aVal, bVal) - } - return String(aVal) === String(bVal) - }) - } - - function isIncludedRoute (current, target) { - return ( - current.path.replace(trailingSlashRE, '/').indexOf( - target.path.replace(trailingSlashRE, '/') - ) === 0 && + if (typeof aVal === "object" && typeof bVal === "object") { + return isObjectEqual(aVal, bVal); + } + + return String(aVal) === String(bVal); + }); +} + +function isIncludedRoute(current, target) { + return ( + current.path + .replace(trailingSlashRE, "/") + .indexOf(target.path.replace(trailingSlashRE, "/")) === 0 && (!target.hash || current.hash === target.hash) && queryIncludes(current.query, target.query) - ) - } - - function queryIncludes (current, target) { - for (var key in target) { + ); +} + +function queryIncludes(current, target) { + for (const key in target) { if (!(key in current)) { - return false - } - } - return true - } - - function handleRouteEntered (route) { - for (var i = 0; i < route.matched.length; i++) { - var record = route.matched[i]; - for (var name in record.instances) { - var instance = record.instances[name]; - var cbs = record.enteredCbs[name]; - if (!instance || !cbs) { continue } - delete record.enteredCbs[name]; - for (var i$1 = 0; i$1 < cbs.length; i$1++) { - if (!instance._isBeingDestroyed) { cbs[i$1](instance); } - } - } - } - } - - // var View = { - // name: 'RouterView', - // functional: true, - // props: { - // name: { - // type: String, - // default: 'default' - // } - // }, - // render: function render (_, ref) { - // var props = ref.props; - // var children = ref.children; - // var parent = ref.parent; - // var data = ref.data; - - // // used by devtools to display a router-view badge - // data.routerView = true; - - // // directly use parent context's createElement() function - // // so that components rendered by router-view can resolve named slots - // var h = parent.$createElement; - // var name = props.name; - // var route = parent.$route; - // var cache = parent._routerViewCache || (parent._routerViewCache = {}); - - // // determine current view depth, also check to see if the tree - // // has been toggled inactive but kept-alive. - // var depth = 0; - // var inactive = false; - // while (parent && parent._routerRoot !== parent) { - // var vnodeData = parent.$vnode ? parent.$vnode.data : {}; - // if (vnodeData.routerView) { - // depth++; - // } - // if (vnodeData.keepAlive && parent._directInactive && parent._inactive) { - // inactive = true; - // } - // parent = parent.$parent; - // } - // data.routerViewDepth = depth; - - // // render previous view if the tree is inactive and kept-alive - // if (inactive) { - // var cachedData = cache[name]; - // var cachedComponent = cachedData && cachedData.component; - // if (cachedComponent) { - // // #2301 - // // pass props - // if (cachedData.configProps) { - // fillPropsinData(cachedComponent, data, cachedData.route, cachedData.configProps); - // } - // return h(cachedComponent, data, children) - // } else { - // // render previous empty view - // return h() - // } - // } - - // var matched = route.matched[depth]; - // var component = matched && matched.components[name]; - - // // render empty node if no matched route or no config component - // if (!matched || !component) { - // cache[name] = null; - // return h() - // } - - // // cache component - // cache[name] = { component: component }; - - // // attach instance registration hook - // // this will be called in the instance's injected lifecycle hooks - // data.registerRouteInstance = function (vm, val) { - // // val could be undefined for unregistration - // var current = matched.instances[name]; - // if ( - // (val && current !== vm) || - // (!val && current === vm) - // ) { - // matched.instances[name] = val; - // } - // } - - // // also register instance in prepatch hook - // // in case the same component instance is reused across different routes - // ;(data.hook || (data.hook = {})).prepatch = function (_, vnode) { - // matched.instances[name] = vnode.componentInstance; - // }; - - // // register instance in init hook - // // in case kept-alive component be actived when routes changed - // data.hook.init = function (vnode) { - // if (vnode.data.keepAlive && - // vnode.componentInstance && - // vnode.componentInstance !== matched.instances[name] - // ) { - // matched.instances[name] = vnode.componentInstance; - // } - - // // if the route transition has already been confirmed then we weren't - // // able to call the cbs during confirmation as the component was not - // // registered yet, so we call it here. - // handleRouteEntered(route); - // }; - - // var configProps = matched.props && matched.props[name]; - // // save route and configProps in cache - // if (configProps) { - // extend(cache[name], { - // route: route, - // configProps: configProps - // }); - // fillPropsinData(component, data, route, configProps); - // } - - // return h(component, data, children) - // } - // }; - - // function fillPropsinData (component, data, route, configProps) { - // // resolve props - // var propsToPass = data.props = resolveProps(route, configProps); - // if (propsToPass) { - // // clone to prevent mutation - // propsToPass = data.props = extend({}, propsToPass); - // // pass non-declared props as attrs - // var attrs = data.attrs = data.attrs || {}; - // for (var key in propsToPass) { - // if (!component.props || !(key in component.props)) { - // attrs[key] = propsToPass[key]; - // delete propsToPass[key]; - // } - // } - // } - // } - - // function resolveProps (route, config) { - // switch (typeof config) { - // case 'undefined': - // return - // case 'object': - // return config - // case 'function': - // return config(route) - // case 'boolean': - // return config ? route.params : undefined - // default: - // { - // warn( - // false, - // "props in \"" + (route.path) + "\" is a " + (typeof config) + ", " + - // "expecting an object, function or boolean." - // ); - // } - // } - // } - - /* */ - - function resolvePath ( - relative, - base, - append - ) { - var firstChar = relative.charAt(0); - if (firstChar === '/') { - return relative - } - - if (firstChar === '?' || firstChar === '#') { - 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]) { + return false; + } + } + + return true; +} + +function handleRouteEntered(route) { + for (let i = 0; i < route.matched.length; i++) { + const record = route.matched[i]; + for (const name in record.instances) { + const instance = record.instances[name]; + const cbs = record.enteredCbs[name]; + if (!instance || !cbs) { + continue; + } + delete record.enteredCbs[name]; + for (let i$1 = 0; i$1 < cbs.length; i$1++) { + if (!instance._isBeingDestroyed) { + cbs[i$1](instance); + } + } + } + } +} + +// var View = { +// name: 'RouterView', +// functional: true, +// props: { +// name: { +// type: String, +// default: 'default' +// } +// }, +// render: function render (_, ref) { +// var props = ref.props; +// var children = ref.children; +// var parent = ref.parent; +// var data = ref.data; + +// // used by devtools to display a router-view badge +// data.routerView = true; + +// // directly use parent context's createElement() function +// // so that components rendered by router-view can resolve named slots +// var h = parent.$createElement; +// var name = props.name; +// var route = parent.$route; +// var cache = parent._routerViewCache || (parent._routerViewCache = {}); + +// // determine current view depth, also check to see if the tree +// // has been toggled inactive but kept-alive. +// var depth = 0; +// var inactive = false; +// while (parent && parent._routerRoot !== parent) { +// var vnodeData = parent.$vnode ? parent.$vnode.data : {}; +// if (vnodeData.routerView) { +// depth++; +// } +// if (vnodeData.keepAlive && parent._directInactive && parent._inactive) { +// inactive = true; +// } +// parent = parent.$parent; +// } +// data.routerViewDepth = depth; + +// // render previous view if the tree is inactive and kept-alive +// if (inactive) { +// var cachedData = cache[name]; +// var cachedComponent = cachedData && cachedData.component; +// if (cachedComponent) { +// // #2301 +// // pass props +// if (cachedData.configProps) { +// fillPropsinData(cachedComponent, data, cachedData.route, cachedData.configProps); +// } +// return h(cachedComponent, data, children) +// } else { +// // render previous empty view +// return h() +// } +// } + +// var matched = route.matched[depth]; +// var component = matched && matched.components[name]; + +// // render empty node if no matched route or no config component +// if (!matched || !component) { +// cache[name] = null; +// return h() +// } + +// // cache component +// cache[name] = { component: component }; + +// // attach instance registration hook +// // this will be called in the instance's injected lifecycle hooks +// data.registerRouteInstance = function (vm, val) { +// // val could be undefined for unregistration +// var current = matched.instances[name]; +// if ( +// (val && current !== vm) || +// (!val && current === vm) +// ) { +// matched.instances[name] = val; +// } +// } + +// // also register instance in prepatch hook +// // in case the same component instance is reused across different routes +// ;(data.hook || (data.hook = {})).prepatch = function (_, vnode) { +// matched.instances[name] = vnode.componentInstance; +// }; + +// // register instance in init hook +// // in case kept-alive component be actived when routes changed +// data.hook.init = function (vnode) { +// if (vnode.data.keepAlive && +// vnode.componentInstance && +// vnode.componentInstance !== matched.instances[name] +// ) { +// matched.instances[name] = vnode.componentInstance; +// } + +// // if the route transition has already been confirmed then we weren't +// // able to call the cbs during confirmation as the component was not +// // registered yet, so we call it here. +// handleRouteEntered(route); +// }; + +// var configProps = matched.props && matched.props[name]; +// // save route and configProps in cache +// if (configProps) { +// extend(cache[name], { +// route: route, +// configProps: configProps +// }); +// fillPropsinData(component, data, route, configProps); +// } + +// return h(component, data, children) +// } +// }; + +// function fillPropsinData (component, data, route, configProps) { +// // resolve props +// var propsToPass = data.props = resolveProps(route, configProps); +// if (propsToPass) { +// // clone to prevent mutation +// propsToPass = data.props = extend({}, propsToPass); +// // pass non-declared props as attrs +// var attrs = data.attrs = data.attrs || {}; +// for (var key in propsToPass) { +// if (!component.props || !(key in component.props)) { +// attrs[key] = propsToPass[key]; +// delete propsToPass[key]; +// } +// } +// } +// } + +// function resolveProps (route, config) { +// switch (typeof config) { +// case 'undefined': +// return +// case 'object': +// return config +// case 'function': +// return config(route) +// case 'boolean': +// return config ? route.params : undefined +// default: +// { +// warn( +// false, +// "props in \"" + (route.path) + "\" is a " + (typeof config) + ", " + +// "expecting an object, function or boolean." +// ); +// } +// } +// } + +/* */ + +function resolvePath(relative, base, append) { + const firstChar = relative.charAt(0); + if (firstChar === "/") { + return relative; + } + + if (firstChar === "?" || firstChar === "#") { + return base + relative; + } + + const 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 === '..') { - stack.pop(); - } else if (segment !== '.') { - 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) { + } + + // resolve relative path + const segments = relative.replace(/^\//, "").split("/"); + for (let i = 0; i < segments.length; i++) { + const segment = segments[i]; + if (segment === "..") { + stack.pop(); + } else if (segment !== ".") { + stack.push(segment); + } + } + + // ensure leading slash + if (stack[0] !== "") { + stack.unshift(""); + } + + return stack.join("/"); +} + +function parsePath(path) { + let hash = ""; + let query = ""; + + const hashIndex = path.indexOf("#"); + if (hashIndex >= 0) { hash = path.slice(hashIndex); path = path.slice(0, hashIndex); - } - - var queryIndex = path.indexOf('?'); - if (queryIndex >= 0) { + } + + const 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, '/') - } - - var isarray = Array.isArray || function (arr) { - return Object.prototype.toString.call(arr) == '[object Array]'; + } + + return { + path, + query, + hash + }; +} + +function cleanPath(path) { + return path.replace(/\/\//g, "/"); +} + +const isarray = + Array.isArray || + function (arr) { + return Object.prototype.toString.call(arr) == "[object Array]"; }; - - /** - * Expose `pathToRegexp`. - */ - var pathToRegexp_1 = 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; + +/** + * Expose `pathToRegexp`. + */ +const pathToRegexp_1 = pathToRegexp; +const parse_1 = parse; +const compile_1 = compile; +const tokensToFunction_1 = tokensToFunction; +const tokensToRegExp_1 = tokensToRegExp; + +/** + * The main path matching regexp utility. + * + * @type {RegExp} + */ +const 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) { + const tokens = []; + let key = 0; + let index = 0; + let path = ""; + const defaultDelimiter = (options && options.delimiter) || "/"; + let res; + + while ((res = PATH_REGEXP.exec(str)) != null) { + const m = res[0]; + const escaped = res[1]; + const 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]; - + path += escaped[1]; + continue; + } + + const next = str[index]; + const prefix = res[2]; + const name = res[3]; + const capture = res[4]; + const group = res[5]; + const modifier = res[6]; + const 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(path); + path = ""; + } + + const partial = prefix != null && next != null && next !== prefix; + const repeat = modifier === "+" || modifier === "*"; + const optional = modifier === "?" || modifier === "*"; + const delimiter = res[2] || defaultDelimiter; + const 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) + ']+?') + name: name || key++, + prefix: prefix || "", + delimiter, + optional, + repeat, + partial, + asterisk: !!asterisk, + pattern: pattern + ? escapeGroup(pattern) + : asterisk + ? ".*" + : `[^${escapeString(delimiter)}]+?` }); - } - - // Match any characters still remaining. - if (index < str.length) { + } + + // Match any characters still remaining. + if (index < str.length) { path += str.substr(index); - } - - // If the path exists, push it onto the end. - if (path) { + } + + // 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), 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, options) { - // 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 + ')$', flags(options)); - } - } - - 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') + } + + 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), options); +} + +/** + * Prettier encoding of URI path segments. + * + * @param {string} + * @return {string} + */ +function encodeURIComponentPretty(str) { + return encodeURI(str).replace( + /[\/?#]/g, + (c) => `%${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, + (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}` + ); +} + +/** + * Expose a method for transforming tokens into the path function. + */ +function tokensToFunction(tokens, options) { + // Compile all the tokens into regexps. + const matches = new Array(tokens.length); + + // Compile all the patterns before compilation. + for (let i = 0; i < tokens.length; i++) { + if (typeof tokens[i] === "object") { + matches[i] = new RegExp( + `^(?:${tokens[i].pattern})$`, + flags(options) + ); + } + } + + return function (obj, opts) { + let path = ""; + const data = obj || {}; + const options = opts || {}; + const encode = options.pretty + ? encodeURIComponentPretty + : encodeURIComponent; + + for (let i = 0; i < tokens.length; i++) { + const token = tokens[i]; + + if (typeof token === "string") { + path += token; + + continue; } - } - - if (isarray(value)) { - if (!token.repeat) { - throw new TypeError('Expected "' + token.name + '" to not repeat, but received `' + JSON.stringify(value) + '`') + + const 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 (value.length === 0) { - if (token.optional) { - continue - } else { - throw new TypeError('Expected "' + token.name + '" to not be empty') - } + + 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 (let 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; } - - 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; + + 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}"` + ); } - - 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 && 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++) { + + 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 && 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. + const groups = path.source.match(/\((?!\?)/g); + + if (groups) { + for (let 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) { + const parts = []; + + for (let 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)) { + } + + const 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); + } + + options = options || {}; + + const strict = options.strict; + const end = options.end !== false; + let route = ""; + + // Iterate over the tokens and create our regexp string. + for (let i = 0; i < tokens.length; i++) { + const 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 + '))?'; + const prefix = escapeString(token.prefix); + let 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 + ')?'; + 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 { + + route += capture; + } + } + + const delimiter = escapeString(options.delimiter || "/"); + const 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)) { + 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) - } - pathToRegexp_1.parse = parse_1; - pathToRegexp_1.compile = compile_1; - pathToRegexp_1.tokensToFunction = tokensToFunction_1; - pathToRegexp_1.tokensToRegExp = tokensToRegExp_1; - - /* */ - - // $flow-disable-line - var regexpCompileCache = Object.create(null); - - function fillParams ( - path, - params, - routeMsg - ) { - params = params || {}; - try { - var filler = - regexpCompileCache[path] || - (regexpCompileCache[path] = pathToRegexp_1.compile(path)); - + } + + 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 + ); +} +pathToRegexp_1.parse = parse_1; +pathToRegexp_1.compile = compile_1; +pathToRegexp_1.tokensToFunction = tokensToFunction_1; +pathToRegexp_1.tokensToRegExp = tokensToRegExp_1; + +/* */ + +// $flow-disable-line +const regexpCompileCache = Object.create(null); + +function fillParams(path, params, routeMsg) { + params = params || {}; + try { + const filler = + regexpCompileCache[path] || + (regexpCompileCache[path] = pathToRegexp_1.compile(path)); + // Fix #2505 resolving asterisk routes { name: 'not-found', params: { pathMatch: '/not-found' }} // and fix #3106 so that you can work with location descriptor object having params.pathMatch equal to empty string - if (typeof params.pathMatch === 'string') { params[0] = params.pathMatch; } - - return filler(params, { pretty: true }) - } catch (e) { + if (typeof params.pathMatch === "string") { + params[0] = params.pathMatch; + } + + return filler(params, { pretty: true }); + } catch (e) { { - // Fix #3072 no warn if `pathMatch` is string - warn(typeof params.pathMatch === 'string', ("missing param for " + routeMsg + ": " + (e.message))); + // Fix #3072 no warn if `pathMatch` is string + warn( + typeof params.pathMatch === "string", + `missing param for ${routeMsg}: ${e.message}` + ); } - return '' - } finally { + + return ""; + } finally { // delete the 0 if it was added delete params[0]; - } - } - - /* */ - - function normalizeLocation ( - raw, - current, - append, - router - ) { - var next = typeof raw === 'string' ? { path: raw } : raw; - // named target - if (next._normalized) { - return next - } else if (next.name) { + } +} + +/* */ + +function normalizeLocation(raw, current, append, router) { + let next = typeof raw === "string" ? { path: raw } : raw; + // named target + if (next._normalized) { + return next; + } else if (next.name) { next = extend({}, raw); - var params = next.params; - if (params && typeof params === 'object') { - next.params = extend({}, params); - } - return next - } - - // relative params - if (!next.path && next.params && current) { + const params = next.params; + if (params && typeof params === "object") { + next.params = extend({}, params); + } + + return next; + } + + // relative params + if (!next.path && next.params && current) { next = extend({}, next); next._normalized = true; - var params$1 = extend(extend({}, current.params), next.params); + const params$1 = extend(extend({}, current.params), next.params); if (current.name) { - next.name = current.name; - next.params = params$1; + next.name = current.name; + next.params = params$1; } else if (current.matched.length) { - var rawPath = current.matched[current.matched.length - 1].path; - next.path = fillParams(rawPath, params$1, ("path " + (current.path))); + const rawPath = current.matched[current.matched.length - 1].path; + next.path = fillParams(rawPath, params$1, `path ${current.path}`); } else { - 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 + warn(false, "relative params navigation requires a current route."); + } + + return next; + } + + const parsedPath = parsePath(next.path || ""); + const basePath = (current && current.path) || "/"; + const path = parsedPath.path ? resolvePath(parsedPath.path, basePath, append || next.append) : basePath; - - var query = resolveQuery( + + const query = resolveQuery( parsedPath.query, next.query, router && router.options.parseQuery - ); - - var hash = next.hash || parsedPath.hash; - if (hash && hash.charAt(0) !== '#') { - hash = "#" + hash; - } - - return { + ); + + let hash = next.hash || parsedPath.hash; + if (hash && hash.charAt(0) !== "#") { + hash = `#${hash}`; + } + + return { _normalized: true, - path: path, - query: query, - hash: hash - } - } - - // var toTypes = [String, Object]; - // var eventTypes = [String, Array]; - - // var noop = function () {}; - - // var warnedCustomSlot; - // var warnedTagProp; - // var warnedEventProp; - - // var Link = { - // name: 'RouterLink', - // props: { - // to: { - // type: toTypes, - // required: true - // }, - // tag: { - // type: String, - // default: 'a' - // }, - // custom: Boolean, - // exact: Boolean, - // exactPath: Boolean, - // append: Boolean, - // replace: Boolean, - // activeClass: String, - // exactActiveClass: String, - // ariaCurrentValue: { - // type: String, - // default: 'page' - // }, - // event: { - // type: eventTypes, - // 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 location = ref.location; - // var route = ref.route; - // var href = ref.href; - - // var classes = {}; - // var globalActiveClass = router.options.linkActiveClass; - // var globalExactActiveClass = router.options.linkExactActiveClass; - // // Support global empty active class - // var activeClassFallback = - // globalActiveClass == null ? 'router-link-active' : globalActiveClass; - // var exactActiveClassFallback = - // globalExactActiveClass == null - // ? 'router-link-exact-active' - // : globalExactActiveClass; - // var activeClass = - // this.activeClass == null ? activeClassFallback : this.activeClass; - // var exactActiveClass = - // this.exactActiveClass == null - // ? exactActiveClassFallback - // : this.exactActiveClass; - - // var compareTarget = route.redirectedFrom - // ? createRoute(null, normalizeLocation(route.redirectedFrom), null, router) - // : route; - - // classes[exactActiveClass] = isSameRoute(current, compareTarget, this.exactPath); - // classes[activeClass] = this.exact || this.exactPath - // ? classes[exactActiveClass] - // : isIncludedRoute(current, compareTarget); - - // var ariaCurrentValue = classes[exactActiveClass] ? this.ariaCurrentValue : null; - - // var handler = function (e) { - // if (guardEvent(e)) { - // if (this$1.replace) { - // router.replace(location, noop); - // } else { - // router.push(location, noop); - // } - // } - // }; - - // 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 }; - - // var scopedSlot = - // !this.$scopedSlots.$hasNormal && - // this.$scopedSlots.default && - // this.$scopedSlots.default({ - // href: href, - // route: route, - // navigate: handler, - // isActive: classes[activeClass], - // isExactActive: classes[exactActiveClass] - // }); - - // if (scopedSlot) { - // if (!this.custom) { - // !warnedCustomSlot && warn(false, 'In Vue Router 4, the v-slot API will by default wrap its content with an element. Use the custom prop to remove this warning:\n\n'); - // warnedCustomSlot = true; - // } - // if (scopedSlot.length === 1) { - // return scopedSlot[0] - // } else if (scopedSlot.length > 1 || !scopedSlot.length) { - // { - // warn( - // false, - // (" with to=\"" + (this.to) + "\" is trying to use a scoped slot but it didn't provide exactly one child. Wrapping the content with a span element.") - // ); - // } - // return scopedSlot.length === 0 ? h() : h('span', {}, scopedSlot) - // } - // } - - // { - // if ('tag' in this.$options.propsData && !warnedTagProp) { - // warn( - // false, - // "'s tag prop is deprecated and has been removed in Vue Router 4. Use the v-slot API to remove this warning: https://next.router.vuejs.org/guide/migration/#removal-of-event-and-tag-props-in-router-link." - // ); - // warnedTagProp = true; - // } - // if ('event' in this.$options.propsData && !warnedEventProp) { - // warn( - // false, - // "'s event prop is deprecated and has been removed in Vue Router 4. Use the v-slot API to remove this warning: https://next.router.vuejs.org/guide/migration/#removal-of-event-and-tag-props-in-router-link." - // ); - // warnedEventProp = true; - // } - // } - - // if (this.tag === 'a') { - // data.on = on; - // data.attrs = { href: href, 'aria-current': ariaCurrentValue }; - // } else { - // // find the first child and apply listener and href - // var a = findAnchor(this.$slots.default); - // if (a) { - // // in case the is a static node - // a.isStatic = false; - // var aData = (a.data = extend({}, a.data)); - // aData.on = aData.on || {}; - // // transform existing events in both objects into arrays so we can push later - // for (var event in aData.on) { - // var handler$1 = aData.on[event]; - // if (event in on) { - // aData.on[event] = Array.isArray(handler$1) ? handler$1 : [handler$1]; - // } - // } - // // append new listeners for router-link - // for (var event$1 in on) { - // if (event$1 in aData.on) { - // // on[event] is always a function - // aData.on[event$1].push(on[event$1]); - // } else { - // aData.on[event$1] = handler; - // } - // } - - // var aAttrs = (a.data.attrs = extend({}, a.data.attrs)); - // aAttrs.href = href; - // aAttrs['aria-current'] = ariaCurrentValue; - // } else { - // // doesn't have child, apply listener to self - // data.on = on; - // } - // } - - // return h(this.tag, data, this.$slots.default) - // } - // }; - - function guardEvent (e) { - // don't redirect with control keys - if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) { return } - // don't redirect when preventDefault called - if (e.defaultPrevented) { return } - // don't redirect on right click - if (e.button !== undefined && e.button !== 0) { return } - // don't redirect if `target="_blank"` - if (e.currentTarget && e.currentTarget.getAttribute) { - var target = e.currentTarget.getAttribute('target'); - if (/\b_blank\b/i.test(target)) { return } - } - // this may be a Weex event which doesn't have this method - if (e.preventDefault) { + path, + query, + hash + }; +} + +// var toTypes = [String, Object]; +// var eventTypes = [String, Array]; + +// var noop = function () {}; + +// var warnedCustomSlot; +// var warnedTagProp; +// var warnedEventProp; + +// var Link = { +// name: 'RouterLink', +// props: { +// to: { +// type: toTypes, +// required: true +// }, +// tag: { +// type: String, +// default: 'a' +// }, +// custom: Boolean, +// exact: Boolean, +// exactPath: Boolean, +// append: Boolean, +// replace: Boolean, +// activeClass: String, +// exactActiveClass: String, +// ariaCurrentValue: { +// type: String, +// default: 'page' +// }, +// event: { +// type: eventTypes, +// 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 location = ref.location; +// var route = ref.route; +// var href = ref.href; + +// var classes = {}; +// var globalActiveClass = router.options.linkActiveClass; +// var globalExactActiveClass = router.options.linkExactActiveClass; +// // Support global empty active class +// var activeClassFallback = +// globalActiveClass == null ? 'router-link-active' : globalActiveClass; +// var exactActiveClassFallback = +// globalExactActiveClass == null +// ? 'router-link-exact-active' +// : globalExactActiveClass; +// var activeClass = +// this.activeClass == null ? activeClassFallback : this.activeClass; +// var exactActiveClass = +// this.exactActiveClass == null +// ? exactActiveClassFallback +// : this.exactActiveClass; + +// var compareTarget = route.redirectedFrom +// ? createRoute(null, normalizeLocation(route.redirectedFrom), null, router) +// : route; + +// classes[exactActiveClass] = isSameRoute(current, compareTarget, this.exactPath); +// classes[activeClass] = this.exact || this.exactPath +// ? classes[exactActiveClass] +// : isIncludedRoute(current, compareTarget); + +// var ariaCurrentValue = classes[exactActiveClass] ? this.ariaCurrentValue : null; + +// var handler = function (e) { +// if (guardEvent(e)) { +// if (this$1.replace) { +// router.replace(location, noop); +// } else { +// router.push(location, noop); +// } +// } +// }; + +// 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 }; + +// var scopedSlot = +// !this.$scopedSlots.$hasNormal && +// this.$scopedSlots.default && +// this.$scopedSlots.default({ +// href: href, +// route: route, +// navigate: handler, +// isActive: classes[activeClass], +// isExactActive: classes[exactActiveClass] +// }); + +// if (scopedSlot) { +// if (!this.custom) { +// !warnedCustomSlot && warn(false, 'In Vue Router 4, the v-slot API will by default wrap its content with an element. Use the custom prop to remove this warning:\n\n'); +// warnedCustomSlot = true; +// } +// if (scopedSlot.length === 1) { +// return scopedSlot[0] +// } else if (scopedSlot.length > 1 || !scopedSlot.length) { +// { +// warn( +// false, +// (" with to=\"" + (this.to) + "\" is trying to use a scoped slot but it didn't provide exactly one child. Wrapping the content with a span element.") +// ); +// } +// return scopedSlot.length === 0 ? h() : h('span', {}, scopedSlot) +// } +// } + +// { +// if ('tag' in this.$options.propsData && !warnedTagProp) { +// warn( +// false, +// "'s tag prop is deprecated and has been removed in Vue Router 4. Use the v-slot API to remove this warning: https://next.router.vuejs.org/guide/migration/#removal-of-event-and-tag-props-in-router-link." +// ); +// warnedTagProp = true; +// } +// if ('event' in this.$options.propsData && !warnedEventProp) { +// warn( +// false, +// "'s event prop is deprecated and has been removed in Vue Router 4. Use the v-slot API to remove this warning: https://next.router.vuejs.org/guide/migration/#removal-of-event-and-tag-props-in-router-link." +// ); +// warnedEventProp = true; +// } +// } + +// if (this.tag === 'a') { +// data.on = on; +// data.attrs = { href: href, 'aria-current': ariaCurrentValue }; +// } else { +// // find the first child and apply listener and href +// var a = findAnchor(this.$slots.default); +// if (a) { +// // in case the is a static node +// a.isStatic = false; +// var aData = (a.data = extend({}, a.data)); +// aData.on = aData.on || {}; +// // transform existing events in both objects into arrays so we can push later +// for (var event in aData.on) { +// var handler$1 = aData.on[event]; +// if (event in on) { +// aData.on[event] = Array.isArray(handler$1) ? handler$1 : [handler$1]; +// } +// } +// // append new listeners for router-link +// for (var event$1 in on) { +// if (event$1 in aData.on) { +// // on[event] is always a function +// aData.on[event$1].push(on[event$1]); +// } else { +// aData.on[event$1] = handler; +// } +// } + +// var aAttrs = (a.data.attrs = extend({}, a.data.attrs)); +// aAttrs.href = href; +// aAttrs['aria-current'] = ariaCurrentValue; +// } else { +// // doesn't have child, apply listener to self +// data.on = on; +// } +// } + +// return h(this.tag, data, this.$slots.default) +// } +// }; + +function guardEvent(e) { + // don't redirect with control keys + if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) { + return; + } + // don't redirect when preventDefault called + if (e.defaultPrevented) { + return; + } + // don't redirect on right click + if (e.button !== undefined && e.button !== 0) { + return; + } + // don't redirect if `target="_blank"` + if (e.currentTarget && e.currentTarget.getAttribute) { + const target = e.currentTarget.getAttribute("target"); + if (/\b_blank\b/i.test(target)) { + return; + } + } + // this may be a Weex event which doesn't have this method + if (e.preventDefault) { 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 && _Vue === Vue) { return } - // install.installed = true; - - // _Vue = Vue; - - // var isDef = function (v) { return v !== undefined; }; - - // var registerInstance = function (vm, callVal) { - // var i = vm.$options._parentVnode; - // if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) { - // i(vm, callVal); - // } - // }; - - // Vue.mixin({ - // beforeCreate: function beforeCreate () { - // if (isDef(this.$options.router)) { - // this._routerRoot = this; - // this._router = this.$options.router; - // this._router.init(this); - // Vue.util.defineReactive(this, '_route', this._router.history.current); - // } else { - // this._routerRoot = (this.$parent && this.$parent._routerRoot) || this; - // } - // registerInstance(this, this); - // }, - // destroyed: function destroyed () { - // registerInstance(this); - // } - // }); - - // Object.defineProperty(Vue.prototype, '$router', { - // get: function get () { return this._routerRoot._router } - // }); - - // Object.defineProperty(Vue.prototype, '$route', { - // get: function get () { return this._routerRoot._route } - // }); - - // Vue.component('RouterView', View); - // Vue.component('RouterLink', Link); - - // var strats = Vue.config.optionMergeStrategies; - // // use the same hook merging strategy for route hooks - // strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created; - // } - - /* */ - - var inBrowser = typeof window !== 'undefined'; - - /* */ - - function createRouteMap ( - routes, - oldPathList, - oldPathMap, - oldNameMap, - parentRoute - ) { - // the path list is used to control path matching priority - var pathList = oldPathList || []; - // $flow-disable-line - var pathMap = oldPathMap || Object.create(null); - // $flow-disable-line - var nameMap = oldNameMap || Object.create(null); - - routes.forEach(function (route) { + } + + return true; +} + +function findAnchor(children) { + if (children) { + let child; + for (let 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 && _Vue === Vue) { return } +// install.installed = true; + +// _Vue = Vue; + +// var isDef = function (v) { return v !== undefined; }; + +// var registerInstance = function (vm, callVal) { +// var i = vm.$options._parentVnode; +// if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) { +// i(vm, callVal); +// } +// }; + +// Vue.mixin({ +// beforeCreate: function beforeCreate () { +// if (isDef(this.$options.router)) { +// this._routerRoot = this; +// this._router = this.$options.router; +// this._router.init(this); +// Vue.util.defineReactive(this, '_route', this._router.history.current); +// } else { +// this._routerRoot = (this.$parent && this.$parent._routerRoot) || this; +// } +// registerInstance(this, this); +// }, +// destroyed: function destroyed () { +// registerInstance(this); +// } +// }); + +// Object.defineProperty(Vue.prototype, '$router', { +// get: function get () { return this._routerRoot._router } +// }); + +// Object.defineProperty(Vue.prototype, '$route', { +// get: function get () { return this._routerRoot._route } +// }); + +// Vue.component('RouterView', View); +// Vue.component('RouterLink', Link); + +// var strats = Vue.config.optionMergeStrategies; +// // use the same hook merging strategy for route hooks +// strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created; +// } + +/* */ + +const inBrowser = typeof window !== "undefined"; + +/* */ + +function createRouteMap( + routes, + oldPathList, + oldPathMap, + oldNameMap, + parentRoute +) { + // the path list is used to control path matching priority + const pathList = oldPathList || []; + // $flow-disable-line + const pathMap = oldPathMap || Object.create(null); + // $flow-disable-line + const nameMap = oldNameMap || Object.create(null); + + routes.forEach((route) => { addRouteRecord(pathList, pathMap, nameMap, route, parentRoute); - }); - - // ensure wildcard routes are always at the end - for (var i = 0, l = pathList.length; i < l; i++) { - if (pathList[i] === '*') { - pathList.push(pathList.splice(i, 1)[0]); - l--; - i--; - } - } - - { + }); + + // ensure wildcard routes are always at the end + for (let i = 0, l = pathList.length; i < l; i++) { + if (pathList[i] === "*") { + pathList.push(pathList.splice(i, 1)[0]); + l--; + i--; + } + } + + { // warn if routes do not include leading slashes - var found = pathList - // check for missing leading slash - .filter(function (path) { return path && path.charAt(0) !== '*' && path.charAt(0) !== '/'; }); - + const found = pathList + // check for missing leading slash + .filter( + (path) => + path && path.charAt(0) !== "*" && path.charAt(0) !== "/" + ); + if (found.length > 0) { - var pathNames = found.map(function (path) { return ("- " + path); }).join('\n'); - warn(false, ("Non-nested routes must include a leading slash character. Fix the following routes: \n" + pathNames)); - } - } - - return { - pathList: pathList, - pathMap: pathMap, - nameMap: nameMap - } - } - - function addRouteRecord ( - pathList, - pathMap, - nameMap, - route, - parent, - matchAs - ) { - var path = route.path; - var name = route.name; - { - assert(path != null, "\"path\" is required in a route configuration."); + const pathNames = found.map((path) => `- ${path}`).join("\n"); + warn( + false, + `Non-nested routes must include a leading slash character. Fix the following routes: \n${pathNames}` + ); + } + } + + return { + pathList, + pathMap, + nameMap + }; +} + +function addRouteRecord(pathList, pathMap, nameMap, route, parent, matchAs) { + const path = route.path; + const name = route.name; + { + 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." + typeof route.component !== "string", + `route config "component" for path: ${String( + path || name + )} cannot be a ` + "string id. Use an actual component instead." ); - + warn( - // eslint-disable-next-line no-control-regex - !/[^\u0000-\u007F]+/.test(path), - "Route with path \"" + path + "\" contains unencoded characters, make sure " + - "your path is correctly encoded before passing it to the router. Use " + - "encodeURI to encode static segments of your path." + // eslint-disable-next-line no-control-regex + !/[^\u0000-\u007F]+/.test(path), + `Route with path "${path}" contains unencoded characters, make sure ` + + "your path is correctly encoded before passing it to the router. Use " + + "encodeURI to encode static segments of your path." ); - } - - var pathToRegexpOptions = - route.pathToRegexpOptions || {}; - var normalizedPath = normalizePath(path, parent, pathToRegexpOptions.strict); - - if (typeof route.caseSensitive === 'boolean') { + } + + const pathToRegexpOptions = route.pathToRegexpOptions || {}; + const normalizedPath = normalizePath( + path, + parent, + pathToRegexpOptions.strict + ); + + if (typeof route.caseSensitive === "boolean") { pathToRegexpOptions.sensitive = route.caseSensitive; - } - - var record = { + } + + const record = { path: normalizedPath, regex: compileRouteRegex(normalizedPath, pathToRegexpOptions), components: route.components || { default: route.component }, alias: route.alias - ? typeof route.alias === 'string' - ? [route.alias] - : route.alias - : [], + ? typeof route.alias === "string" + ? [route.alias] + : route.alias + : [], instances: {}, enteredCbs: {}, - name: name, - parent: parent, - matchAs: matchAs, + name, + parent, + matchAs, redirect: route.redirect, beforeEnter: route.beforeEnter, meta: route.meta || {}, props: - route.props == null - ? {} - : route.components - ? route.props - : { default: route.props } - }; - - if (route.children) { + route.props == null + ? {} + : route.components + ? route.props + : { default: route.props } + }; + + if (route.children) { // Warn if route is named, does not redirect 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 ( - route.name && - !route.redirect && - route.children.some(function (child) { return /^\/?$/.test(child.path); }) - ) { - warn( - false, - "Named Route '" + (route.name) + "' has a default child route. " + - "When navigating to this named route (:to=\"{name: '" + (route.name) + "'\"), " + - "the default child route will not be rendered. Remove the name from " + - "this route and use the name of the default child route for named " + - "links instead." - ); - } + if ( + route.name && + !route.redirect && + route.children.some((child) => /^\/?$/.test(child.path)) + ) { + warn( + false, + `Named Route '${route.name}' has a default child route. ` + + `When navigating to this named route (:to="{name: '${route.name}'"), ` + + "the default child route will not be rendered. Remove the name from " + + "this route and use the name of the default child route for named " + + "links instead." + ); + } } - route.children.forEach(function (child) { - var childMatchAs = matchAs - ? cleanPath((matchAs + "/" + (child.path))) - : undefined; - addRouteRecord(pathList, pathMap, nameMap, child, record, childMatchAs); + route.children.forEach((child) => { + const childMatchAs = matchAs + ? cleanPath(`${matchAs}/${child.path}`) + : undefined; + addRouteRecord( + pathList, + pathMap, + nameMap, + child, + record, + childMatchAs + ); }); - } - - if (!pathMap[record.path]) { + } + + if (!pathMap[record.path]) { pathList.push(record.path); pathMap[record.path] = record; - } - - if (route.alias !== undefined) { - var aliases = Array.isArray(route.alias) ? route.alias : [route.alias]; - for (var i = 0; i < aliases.length; ++i) { - var alias = aliases[i]; - if (alias === path) { - warn( - false, - ("Found an alias with the same value as the path: \"" + path + "\". You have to remove that alias. It will be ignored in development.") + } + + if (route.alias !== undefined) { + const aliases = Array.isArray(route.alias) + ? route.alias + : [route.alias]; + for (let i = 0; i < aliases.length; ++i) { + const alias = aliases[i]; + if (alias === path) { + warn( + false, + `Found an alias with the same value as the path: "${path}". You have to remove that alias. It will be ignored in development.` + ); + // skip in dev to make it work + continue; + } + + const aliasRoute = { + path: alias, + children: route.children + }; + addRouteRecord( + pathList, + pathMap, + nameMap, + aliasRoute, + parent, + record.path || "/" // matchAs ); - // skip in dev to make it work - continue - } - - var aliasRoute = { - path: alias, - children: route.children - }; - addRouteRecord( - pathList, - pathMap, - nameMap, - aliasRoute, - parent, - record.path || '/' // matchAs - ); - } - } - - if (name) { + } + } + + if (name) { if (!nameMap[name]) { - nameMap[name] = record; + nameMap[name] = record; } else if (!matchAs) { - warn( - false, - "Duplicate named routes definition: " + - "{ name: \"" + name + "\", path: \"" + (record.path) + "\" }" - ); - } - } - } - - function compileRouteRegex ( - path, - pathToRegexpOptions - ) { - var regex = pathToRegexp_1(path, [], pathToRegexpOptions); - { - var keys = Object.create(null); - regex.keys.forEach(function (key) { - warn( - !keys[key.name], - ("Duplicate param keys in route with path: \"" + path + "\"") - ); - keys[key.name] = true; + warn( + false, + `${ + "Duplicate named routes definition: " + '{ name: "' + }${name}", path: "${record.path}" }` + ); + } + } +} + +function compileRouteRegex(path, pathToRegexpOptions) { + const regex = pathToRegexp_1(path, [], pathToRegexpOptions); + { + const keys = Object.create(null); + regex.keys.forEach((key) => { + warn( + !keys[key.name], + `Duplicate param keys in route with path: "${path}"` + ); + keys[key.name] = true; }); - } - return regex - } - - function normalizePath ( - path, - parent, - strict - ) { - if (!strict) { path = path.replace(/\/$/, ''); } - if (path[0] === '/') { return path } - if (parent == null) { return path } - return cleanPath(((parent.path) + "/" + path)) - } - - /* */ - - - - function createMatcher ( - routes, - router - ) { - var ref = createRouteMap(routes); - var pathList = ref.pathList; - var pathMap = ref.pathMap; - var nameMap = ref.nameMap; - - function addRoutes (routes) { + } + + return regex; +} + +function normalizePath(path, parent, strict) { + if (!strict) { + path = path.replace(/\/$/, ""); + } + if (path[0] === "/") { + return path; + } + if (parent == null) { + return path; + } + + return cleanPath(`${parent.path}/${path}`); +} + +/* */ + +function createMatcher(routes, router) { + const ref = createRouteMap(routes); + const pathList = ref.pathList; + const pathMap = ref.pathMap; + const nameMap = ref.nameMap; + + function addRoutes(routes) { createRouteMap(routes, pathList, pathMap, nameMap); - } - - function addRoute (parentOrRoute, route) { - var parent = (typeof parentOrRoute !== 'object') ? nameMap[parentOrRoute] : undefined; + } + + function addRoute(parentOrRoute, route) { + const parent = + typeof parentOrRoute !== "object" + ? nameMap[parentOrRoute] + : undefined; // $flow-disable-line - createRouteMap([route || parentOrRoute], pathList, pathMap, nameMap, parent); - - // add aliases of parent - if (parent && parent.alias.length) { - createRouteMap( - // $flow-disable-line route is defined if parent is - parent.alias.map(function (alias) { return ({ path: alias, children: [route] }); }), + createRouteMap( + [route || parentOrRoute], pathList, pathMap, nameMap, parent - ); - } - } - - function getRoutes () { - return pathList.map(function (path) { return pathMap[path]; }) - } - - function match ( - raw, - currentRoute, - redirectedFrom - ) { - var location = normalizeLocation(raw, currentRoute, false, router); - var name = location.name; - + ); + + // add aliases of parent + if (parent && parent.alias.length) { + createRouteMap( + // $flow-disable-line route is defined if parent is + parent.alias.map((alias) => { + return { path: alias, children: [route] }; + }), + pathList, + pathMap, + nameMap, + parent + ); + } + } + + function getRoutes() { + return pathList.map((path) => pathMap[path]); + } + + function match(raw, currentRoute, redirectedFrom) { + const location = normalizeLocation(raw, currentRoute, false, router); + const name = location.name; + if (name) { - var record = nameMap[name]; - { - warn(record, ("Route with name '" + name + "' does not exist")); - } - if (!record) { return _createRoute(null, location) } - var paramNames = record.regex.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]; - } + const record = nameMap[name]; + { + warn(record, `Route with name '${name}' does not exist`); + } + if (!record) { + return _createRoute(null, location); + } + const paramNames = record.regex.keys + .filter((key) => !key.optional) + .map((key) => key.name); + + if (typeof location.params !== "object") { + location.params = {}; } - } - - location.path = fillParams(record.path, location.params, ("named route \"" + name + "\"")); - return _createRoute(record, location, redirectedFrom) + + if (currentRoute && typeof currentRoute.params === "object") { + for (const key in currentRoute.params) { + if ( + !(key in location.params) && + paramNames.indexOf(key) > -1 + ) { + location.params[key] = currentRoute.params[key]; + } + } + } + + location.path = fillParams( + record.path, + location.params, + `named route "${name}"` + ); + + return _createRoute(record, location, redirectedFrom); } else if (location.path) { - location.params = {}; - for (var i = 0; i < pathList.length; i++) { - var path = pathList[i]; - var record$1 = pathMap[path]; - if (matchRoute(record$1.regex, location.path, location.params)) { - return _createRoute(record$1, location, redirectedFrom) + location.params = {}; + for (let i = 0; i < pathList.length; i++) { + const path = pathList[i]; + const record$1 = pathMap[path]; + if ( + matchRoute(record$1.regex, location.path, location.params) + ) { + return _createRoute(record$1, 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, null, router)) - : originalRedirect; - - if (typeof redirect === 'string') { - redirect = { path: redirect }; - } - - if (!redirect || typeof redirect !== 'object') { - { - 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; - + return _createRoute(null, location); + } + + function redirect(record, location) { + const originalRedirect = record.redirect; + let redirect = + typeof originalRedirect === "function" + ? originalRedirect(createRoute(record, location, null, router)) + : originalRedirect; + + if (typeof redirect === "string") { + redirect = { path: redirect }; + } + + if (!redirect || typeof redirect !== "object") { + { + warn( + false, + `invalid redirect option: ${JSON.stringify(redirect)}` + ); + } + + return _createRoute(null, location); + } + + const re = redirect; + const name = re.name; + const path = re.path; + let query = location.query; + let hash = location.hash; + let 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]; - { - assert(targetRecord, ("redirect failed: named route \"" + name + "\" not found.")); - } - return match({ - _normalized: true, - name: name, - query: query, - hash: hash, - params: params - }, undefined, location) + // resolved named direct + const targetRecord = nameMap[name]; + { + assert( + targetRecord, + `redirect failed: named route "${name}" not found.` + ); + } + + return match( + { + _normalized: true, + name, + query, + hash, + 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) + // 1. resolve relative redirect + const rawPath = resolveRecordPath(path, record); + // 2. resolve params + const resolvedPath = fillParams( + rawPath, + params, + `redirect route with path "${rawPath}"` + ); + // 3. rematch with existing query and hash + return match( + { + _normalized: true, + path: resolvedPath, + query, + 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 + { + warn( + false, + `invalid redirect option: ${JSON.stringify(redirect)}` + ); + } + + return _createRoute(null, location); + } + } + + function alias(record, location, matchAs) { + const aliasedPath = fillParams( + matchAs, + location.params, + `aliased route with path "${matchAs}"` + ); + const 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 - ) { + const matched = aliasedMatch.matched; + const 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) + return redirect(record, redirectedFrom || location); } if (record && record.matchAs) { - return alias(record, location, record.matchAs) - } - return createRoute(record, location, redirectedFrom, router) - } - - return { - match: match, - addRoute: addRoute, - getRoutes: getRoutes, - addRoutes: addRoutes - } - } - - function matchRoute ( - regex, - path, - params - ) { - var m = path.match(regex); - - if (!m) { - return false - } else if (!params) { - return true - } - - for (var i = 1, len = m.length; i < len; ++i) { - var key = regex.keys[i - 1]; + return alias(record, location, record.matchAs); + } + + return createRoute(record, location, redirectedFrom, router); + } + + return { + match, + addRoute, + getRoutes, + addRoutes + }; +} + +function matchRoute(regex, path, params) { + const m = path.match(regex); + + if (!m) { + return false; + } else if (!params) { + return true; + } + + for (let i = 1, len = m.length; i < len; ++i) { + const key = regex.keys[i - 1]; if (key) { - // Fix #1994: using * with props: true generates a param named 0 - params[key.name || 'pathMatch'] = typeof m[i] === 'string' ? decode(m[i]) : m[i]; - } - } - - return true - } - - function resolveRecordPath (path, record) { - return resolvePath(path, record.parent ? record.parent.path : '/', true) - } - - /* */ - - // use User Timing api (if present) for more accurate key precision - var Time = - inBrowser && window.performance && window.performance.now + // Fix #1994: using * with props: true generates a param named 0 + params[key.name || "pathMatch"] = + typeof m[i] === "string" ? decode(m[i]) : m[i]; + } + } + + return true; +} + +function resolveRecordPath(path, record) { + return resolvePath(path, record.parent ? record.parent.path : "/", true); +} + +/* */ + +// use User Timing api (if present) for more accurate key precision +const Time = + inBrowser && window.performance && window.performance.now ? window.performance : Date; - - function genStateKey () { - return Time.now().toFixed(3) - } - - var _key = genStateKey(); - - function getStateKey () { - return _key - } - - function setStateKey (key) { - return (_key = key) - } - - /* */ - - var positionStore = Object.create(null); - - function setupScroll () { - // Prevent browser scroll behavior on History popstate - if ('scrollRestoration' in window.history) { - window.history.scrollRestoration = 'manual'; - } - // Fix for #1585 for Firefox - // Fix for #2195 Add optional third attribute to workaround a bug in safari https://bugs.webkit.org/show_bug.cgi?id=182678 - // Fix for #2774 Support for apps loaded from Windows file shares not mapped to network drives: replaced location.origin with - // window.location.protocol + '//' + window.location.host - // location.host contains the port and location.hostname doesn't - var protocolAndPath = window.location.protocol + '//' + window.location.host; - var absolutePath = window.location.href.replace(protocolAndPath, ''); - // preserve existing history state as it could be overriden by the user - var stateCopy = extend({}, window.history.state); - stateCopy.key = getStateKey(); - window.history.replaceState(stateCopy, '', absolutePath); - window.addEventListener('popstate', handlePopState); - return function () { - window.removeEventListener('popstate', handlePopState); - } - } - - function handleScroll ( - router, - to, - from, - isPop - ) { - if (!router.app) { - return - } - - var behavior = router.options.scrollBehavior; - if (!behavior) { - return - } - - { - assert(typeof behavior === 'function', "scrollBehavior must be a function"); - } - - // wait until re-render finishes before scrolling - BI.nextTick(function () { - var position = getScrollPosition(); - var shouldScroll = behavior.call( - router, - to, - from, - isPop ? position : null + +function genStateKey() { + return Time.now().toFixed(3); +} + +let _key = genStateKey(); + +function getStateKey() { + return _key; +} + +function setStateKey(key) { + return (_key = key); +} + +/* */ + +const positionStore = Object.create(null); + +function setupScroll() { + // Prevent browser scroll behavior on History popstate + if ("scrollRestoration" in window.history) { + window.history.scrollRestoration = "manual"; + } + // Fix for #1585 for Firefox + // Fix for #2195 Add optional third attribute to workaround a bug in safari https://bugs.webkit.org/show_bug.cgi?id=182678 + // Fix for #2774 Support for apps loaded from Windows file shares not mapped to network drives: replaced location.origin with + // window.location.protocol + '//' + window.location.host + // location.host contains the port and location.hostname doesn't + const protocolAndPath = `${window.location.protocol}//${window.location.host}`; + const absolutePath = window.location.href.replace(protocolAndPath, ""); + // preserve existing history state as it could be overriden by the user + const stateCopy = extend({}, window.history.state); + stateCopy.key = getStateKey(); + window.history.replaceState(stateCopy, "", absolutePath); + window.addEventListener("popstate", handlePopState); + + return function () { + window.removeEventListener("popstate", handlePopState); + }; +} + +function handleScroll(router, to, from, isPop) { + if (!router.app) { + return; + } + + const behavior = router.options.scrollBehavior; + if (!behavior) { + return; + } + + { + assert( + typeof behavior === "function", + "scrollBehavior must be a function" + ); + } + + // wait until re-render finishes before scrolling + nextTick(() => { + const position = getScrollPosition(); + const shouldScroll = behavior.call( + router, + to, + from, + isPop ? position : null ); - + if (!shouldScroll) { - return + return; } - - if (typeof shouldScroll.then === 'function') { - shouldScroll - .then(function (shouldScroll) { - scrollToPosition((shouldScroll), position); - }) - .catch(function (err) { - { - assert(false, err.toString()); - } - }); + + if (typeof shouldScroll.then === "function") { + shouldScroll + .then((shouldScroll) => { + scrollToPosition(shouldScroll, position); + }) + .catch((err) => { + { + assert(false, err.toString()); + } + }); } else { - scrollToPosition(shouldScroll, position); + scrollToPosition(shouldScroll, position); } - }); - } - - function saveScrollPosition () { - var key = getStateKey(); - if (key) { + }); +} + +function saveScrollPosition() { + const key = getStateKey(); + if (key) { positionStore[key] = { - x: window.pageXOffset, - y: window.pageYOffset + x: window.pageXOffset, + y: window.pageYOffset }; - } } - - function handlePopState (e) { - saveScrollPosition(); - if (e.state && e.state.key) { +} + +function handlePopState(e) { + saveScrollPosition(); + if (e.state && e.state.key) { setStateKey(e.state.key); - } - } - - function getScrollPosition () { - var key = getStateKey(); - if (key) { - return positionStore[key] - } - } - - function getElementPosition (el, offset) { - var docEl = document.documentElement; - var docRect = docEl.getBoundingClientRect(); - var elRect = el.getBoundingClientRect(); - return { + } +} + +function getScrollPosition() { + const key = getStateKey(); + if (key) { + return positionStore[key]; + } +} + +function getElementPosition(el, offset) { + const docEl = document.documentElement; + const docRect = docEl.getBoundingClientRect(); + const elRect = el.getBoundingClientRect(); + + return { x: elRect.left - docRect.left - offset.x, y: elRect.top - docRect.top - offset.y - } - } - - function isValidPosition (obj) { - return isNumber(obj.x) || isNumber(obj.y) - } - - function normalizePosition (obj) { - return { + }; +} + +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 normalizeOffset (obj) { - return { + }; +} + +function normalizeOffset(obj) { + return { x: isNumber(obj.x) ? obj.x : 0, y: isNumber(obj.y) ? obj.y : 0 - } - } - - function isNumber (v) { - return typeof v === 'number' - } - - var hashStartsWithNumberRE = /^#\d/; - - function scrollToPosition (shouldScroll, position) { - var isObject = typeof shouldScroll === 'object'; - if (isObject && typeof shouldScroll.selector === 'string') { + }; +} + +function isNumber(v) { + return typeof v === "number"; +} + +const hashStartsWithNumberRE = /^#\d/; + +function scrollToPosition(shouldScroll, position) { + const isObject = typeof shouldScroll === "object"; + if (isObject && typeof shouldScroll.selector === "string") { // getElementById would still fail if the selector contains a more complicated query like #main[data-attr] // but at the same time, it doesn't make much sense to select an element with an id and an extra selector - var el = hashStartsWithNumberRE.test(shouldScroll.selector) // $flow-disable-line - ? document.getElementById(shouldScroll.selector.slice(1)) // $flow-disable-line - : document.querySelector(shouldScroll.selector); - + const el = hashStartsWithNumberRE.test(shouldScroll.selector) // $flow-disable-line + ? document.getElementById(shouldScroll.selector.slice(1)) // $flow-disable-line + : document.querySelector(shouldScroll.selector); + if (el) { - var offset = - shouldScroll.offset && typeof shouldScroll.offset === 'object' - ? shouldScroll.offset - : {}; - offset = normalizeOffset(offset); - position = getElementPosition(el, offset); + let offset = + shouldScroll.offset && typeof shouldScroll.offset === "object" + ? shouldScroll.offset + : {}; + offset = normalizeOffset(offset); + position = getElementPosition(el, offset); } else if (isValidPosition(shouldScroll)) { - position = normalizePosition(shouldScroll); + position = normalizePosition(shouldScroll); } - } else if (isObject && isValidPosition(shouldScroll)) { + } else if (isObject && isValidPosition(shouldScroll)) { position = normalizePosition(shouldScroll); - } - - if (position) { + } + + if (position) { // $flow-disable-line - if ('scrollBehavior' in document.documentElement.style) { - window.scrollTo({ - left: position.x, - top: position.y, - // $flow-disable-line - behavior: shouldScroll.behavior - }); + if ("scrollBehavior" in document.documentElement.style) { + window.scrollTo({ + left: position.x, + top: position.y, + // $flow-disable-line + behavior: shouldScroll.behavior + }); } else { - window.scrollTo(position.x, position.y); - } - } - } - - /* */ - - var supportsPushState = - inBrowser && - (function () { - var ua = window.navigator.userAgent; - + window.scrollTo(position.x, position.y); + } + } +} + +/* */ + +const supportsPushState = + inBrowser && + (function () { + const 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 + (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 && typeof window.history.pushState === 'function' - })(); - - function pushState (url, replace) { - saveScrollPosition(); - // try...catch the pushState call to get around Safari - // DOM Exception 18 where it limits to 100 pushState calls - var history = window.history; - try { + return false; + } + + return window.history && typeof window.history.pushState === "function"; + })(); + +function pushState(url, replace) { + saveScrollPosition(); + // try...catch the pushState call to get around Safari + // DOM Exception 18 where it limits to 100 pushState calls + const history = window.history; + try { if (replace) { - // preserve existing history state as it could be overriden by the user - var stateCopy = extend({}, history.state); - stateCopy.key = getStateKey(); - history.replaceState(stateCopy, '', url); + // preserve existing history state as it could be overriden by the user + const stateCopy = extend({}, history.state); + stateCopy.key = getStateKey(); + history.replaceState(stateCopy, "", url); } else { - history.pushState({ key: setStateKey(genStateKey()) }, '', url); + history.pushState({ key: setStateKey(genStateKey()) }, "", url); } - } catch (e) { - window.location[replace ? 'replace' : 'assign'](url); - } - } - - function replaceState (url) { - pushState(url, true); - } - - /* */ - - function runQueue (queue, fn, cb) { - var step = function (index) { + } catch (e) { + window.location[replace ? "replace" : "assign"](url); + } +} + +function replaceState(url) { + pushState(url, true); +} + +/* */ + +function runQueue(queue, fn, cb) { + var step = function (index) { if (index >= queue.length) { - cb(); + cb(); } else { - if (queue[index]) { - fn(queue[index], function () { - step(index + 1); - }); - } else { - step(index + 1); - } - } - }; - step(0); - } - - // When changing thing, also edit router.d.ts - var NavigationFailureType = { - redirected: 2, - aborted: 4, - cancelled: 8, - duplicated: 16 + if (queue[index]) { + fn(queue[index], () => { + step(index + 1); + }); + } else { + step(index + 1); + } + } }; - - function createNavigationRedirectedError (from, to) { - return createRouterError( + step(0); +} + +// When changing thing, also edit router.d.ts +const NavigationFailureType = { + redirected: 2, + aborted: 4, + cancelled: 8, + duplicated: 16 +}; + +function createNavigationRedirectedError(from, to) { + return createRouterError( from, to, NavigationFailureType.redirected, - ("Redirected when going from \"" + (from.fullPath) + "\" to \"" + (stringifyRoute( - to - )) + "\" via a navigation guard.") - ) - } - - function createNavigationDuplicatedError (from, to) { - var error = createRouterError( + `Redirected when going from "${from.fullPath}" to "${stringifyRoute( + to + )}" via a navigation guard.` + ); +} + +function createNavigationDuplicatedError(from, to) { + const error = createRouterError( from, to, NavigationFailureType.duplicated, - ("Avoided redundant navigation to current location: \"" + (from.fullPath) + "\".") - ); - // backwards compatible with the first introduction of Errors - error.name = 'NavigationDuplicated'; - return error - } - - function createNavigationCancelledError (from, to) { - return createRouterError( + `Avoided redundant navigation to current location: "${from.fullPath}".` + ); + // backwards compatible with the first introduction of Errors + error.name = "NavigationDuplicated"; + + return error; +} + +function createNavigationCancelledError(from, to) { + return createRouterError( from, to, NavigationFailureType.cancelled, - ("Navigation cancelled from \"" + (from.fullPath) + "\" to \"" + (to.fullPath) + "\" with a new navigation.") - ) - } - - function createNavigationAbortedError (from, to) { - return createRouterError( + `Navigation cancelled from "${from.fullPath}" to "${to.fullPath}" with a new navigation.` + ); +} + +function createNavigationAbortedError(from, to) { + return createRouterError( from, to, NavigationFailureType.aborted, - ("Navigation aborted from \"" + (from.fullPath) + "\" to \"" + (to.fullPath) + "\" via a navigation guard.") - ) - } - - function createRouterError (from, to, type, message) { - var error = new Error(message); - error._isRouter = true; - error.from = from; - error.to = to; - error.type = type; - - return error - } - - var propertiesToLog = ['params', 'query', 'hash']; - - function stringifyRoute (to) { - if (typeof to === 'string') { return to } - if ('path' in to) { return to.path } - var location = {}; - propertiesToLog.forEach(function (key) { - if (key in to) { location[key] = to[key]; } - }); - return JSON.stringify(location, null, 2) - } - - function isError (err) { - return Object.prototype.toString.call(err).indexOf('Error') > -1 - } - - function isNavigationFailure (err, errorType) { - return ( + `Navigation aborted from "${from.fullPath}" to "${to.fullPath}" via a navigation guard.` + ); +} + +function createRouterError(from, to, type, message) { + const error = new Error(message); + error._isRouter = true; + error.from = from; + error.to = to; + error.type = type; + + return error; +} + +const propertiesToLog = ["params", "query", "hash"]; + +function stringifyRoute(to) { + if (typeof to === "string") { + return to; + } + if ("path" in to) { + return to.path; + } + const location = {}; + propertiesToLog.forEach((key) => { + if (key in to) { + location[key] = to[key]; + } + }); + + return JSON.stringify(location, null, 2); +} + +function isError(err) { + return Object.prototype.toString.call(err).indexOf("Error") > -1; +} + +function isNavigationFailure(err, errorType) { + return ( isError(err) && err._isRouter && (errorType == null || err.type === errorType) - ) - } - - /* */ - - function resolveAsyncComponents (matched) { - return function (to, from, next) { - var hasAsync = false; - var pending = 0; - var error = null; - - flatMapComponents(matched, function (def, _, match, key) { - // if it's a function and doesn't have cid 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.cid === undefined) { - hasAsync = true; - pending++; - - var resolve = once(function (resolvedDef) { - if (isESModule(resolvedDef)) { - resolvedDef = resolvedDef.default; - } - // save resolved on async factory in case it's used elsewhere - def.resolved = resolvedDef; - match.components[key] = resolvedDef; - pending--; - if (pending <= 0) { - next(); - } - }); - - var reject = once(function (reason) { - var msg = "Failed to resolve async component " + key + ": " + reason; - warn(false, msg); - if (!error) { - error = isError(reason) - ? reason - : new Error(msg); - next(error); - } - }); - - var res; - try { - res = def(resolve, reject); - } catch (e) { - reject(e); - } - if (res) { - if (typeof res.then === 'function') { - res.then(resolve, reject); - } else { - // new syntax in Vue 2.3 - var comp = res.component; - if (comp && typeof comp.then === 'function') { - comp.then(resolve, reject); + ); +} + +/* */ + +function resolveAsyncComponents(matched) { + return function (to, from, next) { + let hasAsync = false; + let pending = 0; + let error = null; + + flatMapComponents(matched, (def, _, match, key) => { + // if it's a function and doesn't have cid 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.cid === undefined) { + hasAsync = true; + pending++; + + const resolve = once((resolvedDef) => { + if (isESModule(resolvedDef)) { + resolvedDef = resolvedDef.default; + } + // save resolved on async factory in case it's used elsewhere + def.resolved = resolvedDef; + match.components[key] = resolvedDef; + pending--; + if (pending <= 0) { + next(); + } + }); + + const reject = once((reason) => { + const msg = `Failed to resolve async component ${key}: ${reason}`; + warn(false, msg); + if (!error) { + error = isError(reason) ? reason : new Error(msg); + next(error); + } + }); + + let res; + try { + res = def(resolve, reject); + } catch (e) { + reject(e); + } + if (res) { + if (typeof res.then === "function") { + res.then(resolve, reject); + } else { + // new syntax in Vue 2.3 + const comp = res.component; + if (comp && typeof comp.then === "function") { + comp.then(resolve, reject); + } + } } - } } - } }); - - if (!hasAsync) { next(); } - } - } - - 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 hasSymbol = - typeof Symbol === 'function' && - typeof Symbol.toStringTag === 'symbol'; - - function isESModule (obj) { - return obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === 'Module') - } - - // in Webpack 2, require.ensure now also returns a Promise - // so the resolve/reject functions may get called an extra time - // if the user uses an arrow function shorthand that happens to - // return that Promise. - function once (fn) { - var called = false; - return function () { - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - - if (called) { return } - called = true; - return fn.apply(this, args) - } - } - - /* */ - - 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; - this.ready = false; - this.readyCbs = []; - this.readyErrorCbs = []; - this.errorCbs = []; - this.listeners = []; + + if (!hasAsync) { + next(); + } }; - - History.prototype.listen = function listen (cb) { - this.cb = cb; +} + +function flatMapComponents(matched, fn) { + return flatten( + matched.map((m) => + Object.keys(m.components).map((key) => + fn(m.components[key], m.instances[key], m, key) + ) + ) + ); +} + +function flatten(arr) { + return Array.prototype.concat.apply([], arr); +} + +const hasSymbol = + typeof Symbol === "function" && typeof Symbol.toStringTag === "symbol"; + +function isESModule(obj) { + return ( + obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === "Module") + ); +} + +// in Webpack 2, require.ensure now also returns a Promise +// so the resolve/reject functions may get called an extra time +// if the user uses an arrow function shorthand that happens to +// return that Promise. +function once(fn) { + let called = false; + + return function () { + let args = [], + len = arguments.length; + while (len--) args[len] = arguments[len]; + + if (called) { + return; + } + called = true; + + return fn.apply(this, args); }; - - History.prototype.onReady = function onReady (cb, errorCb) { - if (this.ready) { +} + +/* */ + +const 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; + this.ready = false; + this.readyCbs = []; + this.readyErrorCbs = []; + this.errorCbs = []; + this.listeners = []; +}; + +History.prototype.listen = function listen(cb) { + this.cb = cb; +}; + +History.prototype.onReady = function onReady(cb, errorCb) { + if (this.ready) { cb(); - } else { + } else { this.readyCbs.push(cb); if (errorCb) { - this.readyErrorCbs.push(errorCb); + this.readyErrorCbs.push(errorCb); } - } - }; - - History.prototype.onError = function onError (errorCb) { - this.errorCbs.push(errorCb); - }; - - History.prototype.transitionTo = function transitionTo ( - location, - onComplete, - onAbort - ) { - var this$1 = this; - - var route; - // catch redirect option https://github.com/vuejs/vue-router/issues/3201 - try { + } +}; + +History.prototype.onError = function onError(errorCb) { + this.errorCbs.push(errorCb); +}; + +History.prototype.transitionTo = function transitionTo( + location, + onComplete, + onAbort +) { + const this$1 = this; + + let route; + // catch redirect option https://github.com/vuejs/vue-router/issues/3201 + try { route = this.router.match(location, this.current); - } catch (e) { - this.errorCbs.forEach(function (cb) { - cb(e); + } catch (e) { + this.errorCbs.forEach((cb) => { + cb(e); }); // Exception should still be thrown - throw e - } - var prev = this.current; - this.confirmTransition( + throw e; + } + const prev = this.current; + this.confirmTransition( route, - function () { - this$1.updateRoute(route); - onComplete && onComplete(route); - this$1.ensureURL(); - this$1.router.afterHooks.forEach(function (hook) { - hook && hook(route, prev); - }); - - // fire ready cbs once - if (!this$1.ready) { - this$1.ready = true; - this$1.readyCbs.forEach(function (cb) { - cb(route); + () => { + this$1.updateRoute(route); + onComplete && onComplete(route); + this$1.ensureURL(); + this$1.router.afterHooks.forEach((hook) => { + hook && hook(route, prev); }); - } + + // fire ready cbs once + if (!this$1.ready) { + this$1.ready = true; + this$1.readyCbs.forEach((cb) => { + cb(route); + }); + } }, - function (err) { - if (onAbort) { - onAbort(err); - } - if (err && !this$1.ready) { - // Initial redirection should not mark the history as ready yet - // because it's triggered by the redirection instead - // https://github.com/vuejs/vue-router/issues/3225 - // https://github.com/vuejs/vue-router/issues/3331 - if (!isNavigationFailure(err, NavigationFailureType.redirected) || prev !== START) { - this$1.ready = true; - this$1.readyErrorCbs.forEach(function (cb) { - cb(err); - }); + (err) => { + if (onAbort) { + onAbort(err); + } + if (err && !this$1.ready) { + // Initial redirection should not mark the history as ready yet + // because it's triggered by the redirection instead + // https://github.com/vuejs/vue-router/issues/3225 + // https://github.com/vuejs/vue-router/issues/3331 + if ( + !isNavigationFailure( + err, + NavigationFailureType.redirected + ) || + prev !== START + ) { + this$1.ready = true; + this$1.readyErrorCbs.forEach((cb) => { + cb(err); + }); + } } - } } - ); - }; - - History.prototype.confirmTransition = function confirmTransition (route, onComplete, onAbort) { - var this$1 = this; - - var current = this.current; - this.pending = route; - var abort = function (err) { + ); +}; + +History.prototype.confirmTransition = function confirmTransition( + route, + onComplete, + onAbort +) { + const this$1 = this; + + const current = this.current; + this.pending = route; + const abort = function (err) { // changed after adding errors with // https://github.com/vuejs/vue-router/pull/3047 before that change, // redirect and aborted navigation would produce an err == null if (!isNavigationFailure(err) && isError(err)) { - if (this$1.errorCbs.length) { - this$1.errorCbs.forEach(function (cb) { - cb(err); - }); - } else { - warn(false, 'uncaught error during route navigation:'); - console.error(err); - } + if (this$1.errorCbs.length) { + this$1.errorCbs.forEach((cb) => { + cb(err); + }); + } else { + warn(false, "uncaught error during route navigation:"); + console.error(err); + } } onAbort && onAbort(err); - }; - var lastRouteIndex = route.matched.length - 1; - var lastCurrentIndex = current.matched.length - 1; - if ( + }; + const lastRouteIndex = route.matched.length - 1; + const lastCurrentIndex = current.matched.length - 1; + if ( isSameRoute(route, current) && // in the case the route map has been dynamically appended to lastRouteIndex === lastCurrentIndex && route.matched[lastRouteIndex] === current.matched[lastCurrentIndex] - ) { + ) { this.ensureURL(); + return; - } - - var ref = resolveQueue( - this.current.matched, - route.matched - ); - var updated = ref.updated; - var deactivated = ref.deactivated; - var activated = ref.activated; - - var queue = [].concat( + } + + const ref = resolveQueue(this.current.matched, route.matched); + const updated = ref.updated; + const deactivated = ref.deactivated; + const activated = ref.activated; + + const queue = [].concat( // in-component leave guards extractLeaveGuards(deactivated), // global before hooks @@ -2345,864 +2466,912 @@ // in-component update hooks extractUpdateHooks(updated), // in-config enter guards - activated.map(function (m) { return m.beforeEnter; }), + activated.map((m) => m.beforeEnter), // async components resolveAsyncComponents(activated) - ); - - var iterator = function (hook, next) { + ); + + const iterator = function (hook, next) { if (this$1.pending !== route) { - return abort(createNavigationCancelledError(current, route)) + return abort(createNavigationCancelledError(current, route)); } try { - hook(route, current, function (to) { - if (to === false) { - // next(false) -> abort navigation, ensure current URL - this$1.ensureURL(true); - abort(createNavigationAbortedError(current, route)); - } else if (isError(to)) { - this$1.ensureURL(true); - abort(to); - } else if ( - typeof to === 'string' || - (typeof to === 'object' && - (typeof to.path === 'string' || typeof to.name === 'string')) - ) { - // next('/') or next({ path: '/' }) -> redirect - abort(createNavigationRedirectedError(current, route)); - if (typeof to === 'object' && to.replace) { - this$1.replace(to); - } else { - this$1.push(to); - } - } else { - // confirm transition and pass on the value - next(to); - } - }); + hook(route, current, (to) => { + if (to === false) { + // next(false) -> abort navigation, ensure current URL + this$1.ensureURL(true); + abort(createNavigationAbortedError(current, route)); + } else if (isError(to)) { + this$1.ensureURL(true); + abort(to); + } else if ( + typeof to === "string" || + (typeof to === "object" && + (typeof to.path === "string" || + typeof to.name === "string")) + ) { + // next('/') or next({ path: '/' }) -> redirect + abort(createNavigationRedirectedError(current, route)); + if (typeof to === "object" && to.replace) { + this$1.replace(to); + } else { + this$1.push(to); + } + } else { + // confirm transition and pass on the value + next(to); + } + }); } catch (e) { - abort(e); + abort(e); } - }; - - runQueue(queue, iterator, function () { + }; + + runQueue(queue, iterator, () => { // wait until async components are resolved before // extracting in-component enter guards - var enterGuards = extractEnterGuards(activated); - var queue = enterGuards.concat(this$1.router.resolveHooks); - runQueue(queue, iterator, function () { - if (this$1.pending !== route) { - return abort(createNavigationCancelledError(current, route)) - } - this$1.pending = null; - onComplete(route); - if (this$1.router.app) { - BI.nextTick(function () { - handleRouteEntered(route); - }); - } + const enterGuards = extractEnterGuards(activated); + const queue = enterGuards.concat(this$1.router.resolveHooks); + runQueue(queue, iterator, () => { + if (this$1.pending !== route) { + return abort(createNavigationCancelledError(current, route)); + } + this$1.pending = null; + onComplete(route); + if (this$1.router.app) { + nextTick(() => { + handleRouteEntered(route); + }); + } }); - }); - }; - - History.prototype.updateRoute = function updateRoute (route) { - this.current = route; - this.cb && this.cb(route); - }; - - History.prototype.setupListeners = function setupListeners () { - // Default implementation is empty - }; - - History.prototype.teardown = function teardown () { - // clean up event listeners - // https://github.com/vuejs/vue-router/issues/2341 - this.listeners.forEach(function (cleanupListener) { + }); +}; + +History.prototype.updateRoute = function updateRoute(route) { + this.current = route; + this.cb && this.cb(route); +}; + +History.prototype.setupListeners = function setupListeners() { + // Default implementation is empty +}; + +History.prototype.teardown = function teardown() { + // clean up event listeners + // https://github.com/vuejs/vue-router/issues/2341 + this.listeners.forEach((cleanupListener) => { cleanupListener(); - }); - this.listeners = []; - - // reset current history route - // https://github.com/vuejs/vue-router/issues/3294 - this.current = START; - this.pending = null; - }; - - function normalizeBase (base) { - if (!base) { + }); + this.listeners = []; + + // reset current history route + // https://github.com/vuejs/vue-router/issues/3294 + this.current = START; + this.pending = null; +}; + +function normalizeBase(base) { + if (!base) { if (inBrowser) { - // respect tag - var baseEl = document.querySelector('base'); - base = (baseEl && baseEl.getAttribute('href')) || '/'; - // strip full URL origin - base = base.replace(/^https?:\/\/[^\/]+/, ''); + // respect tag + const baseEl = document.querySelector("base"); + base = (baseEl && baseEl.getAttribute("href")) || "/"; + // strip full URL origin + base = base.replace(/^https?:\/\/[^\/]+/, ""); } 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++) { + base = "/"; + } + } + // make sure there's the starting slash + if (base.charAt(0) !== "/") { + base = `/${base}`; + } + // remove trailing slash + return base.replace(/\/$/, ""); +} + +function resolveQueue(current, next) { + let i; + const max = Math.max(current.length, next.length); + for (i = 0; i < max; i++) { if (current[i] !== next[i]) { - break + break; } - } - return { + } + + return { updated: next.slice(0, i), activated: next.slice(i), deactivated: current.slice(i) - } - } - - function extractGuards ( - records, - name, - bind, - reverse - ) { - var guards = flatMapComponents(records, function (def, instance, match, key) { - var guard = extractGuard(def, name); + }; +} + +function extractGuards(records, name, bind, reverse) { + const guards = flatMapComponents(records, (def, instance, match, key) => { + const guard = extractGuard(def, name); if (guard) { - return Array.isArray(guard) - ? guard.map(function (guard) { return bind(guard, instance, match, key); }) - : bind(guard, instance, match, key) + return Array.isArray(guard) + ? guard.map((guard) => bind(guard, instance, match, key)) + : bind(guard, instance, match, key); } - }); - return flatten(reverse ? guards.reverse() : guards) - } - - function extractGuard ( - def, - key - ) { - if (typeof def !== 'function') { + }); + + return flatten(reverse ? guards.reverse() : guards); +} + +function extractGuard(def, key) { + if (typeof def !== "function") { // extend now so that global mixins are applied. // def = _Vue.extend(def); - } - return def[key] } - - function extractLeaveGuards (deactivated) { - return extractGuards(deactivated, 'beforeRouteLeave', bindGuard, true) - } - - function extractUpdateHooks (updated) { - return extractGuards(updated, 'beforeRouteUpdate', bindGuard) - } - - function bindGuard (guard, instance) { - if (instance) { - return function boundRouteGuard () { - return guard.apply(instance, arguments) - } - } + + return def[key]; +} + +function extractLeaveGuards(deactivated) { + return extractGuards(deactivated, "beforeRouteLeave", bindGuard, true); +} + +function extractUpdateHooks(updated) { + return extractGuards(updated, "beforeRouteUpdate", bindGuard); +} + +function bindGuard(guard, instance) { + if (instance) { + return function boundRouteGuard() { + return guard.apply(instance, arguments); + }; } - - function extractEnterGuards ( - activated - ) { - return extractGuards( +} + +function extractEnterGuards(activated) { + return extractGuards( activated, - 'beforeRouteEnter', - function (guard, _, match, key) { - return bindEnterGuard(guard, match, key) - } - ) - } - - function bindEnterGuard ( - guard, - match, - key - ) { - return function routeEnterGuard (to, from, next) { - return guard(to, from, function (cb) { - if (typeof cb === 'function') { - if (!match.enteredCbs[key]) { - match.enteredCbs[key] = []; + "beforeRouteEnter", + (guard, _, match, key) => bindEnterGuard(guard, match, key) + ); +} + +function bindEnterGuard(guard, match, key) { + return function routeEnterGuard(to, from, next) { + return guard(to, from, (cb) => { + if (typeof cb === "function") { + if (!match.enteredCbs[key]) { + match.enteredCbs[key] = []; + } + match.enteredCbs[key].push(cb); } - match.enteredCbs[key].push(cb); - } - next(cb); - }) - } - } - - /* */ - - var HTML5History = /*@__PURE__*/(function (History) { - function HTML5History (router, base) { + next(cb); + }); + }; +} + +/* */ + +const HTML5History = /* @__PURE__*/ (function (History) { + function HTML5History(router, base) { History.call(this, router, base); - + this._startLocation = getLocation(this.base); - } - - if ( History ) HTML5History.__proto__ = History; - HTML5History.prototype = Object.create( History && History.prototype ); - HTML5History.prototype.constructor = HTML5History; - - HTML5History.prototype.setupListeners = function setupListeners () { - var this$1 = this; - + } + + if (History) HTML5History.__proto__ = History; + HTML5History.prototype = Object.create(History && History.prototype); + HTML5History.prototype.constructor = HTML5History; + + HTML5History.prototype.setupListeners = function setupListeners() { + const this$1 = this; + if (this.listeners.length > 0) { - return + return; } - - var router = this.router; - var expectScroll = router.options.scrollBehavior; - var supportsScroll = supportsPushState && expectScroll; - + + const router = this.router; + const expectScroll = router.options.scrollBehavior; + const supportsScroll = supportsPushState && expectScroll; + if (supportsScroll) { - this.listeners.push(setupScroll()); - } - - var handleRoutingEvent = function () { - var current = this$1.current; - - // Avoiding first `popstate` event dispatched in some browsers but first - // history route not updated since async guard at the same time. - var location = getLocation(this$1.base); - if (this$1.current === START && location === this$1._startLocation) { - return - } - - this$1.transitionTo(location, function (route) { - if (supportsScroll) { - handleScroll(router, route, current, true); + this.listeners.push(setupScroll()); + } + + const handleRoutingEvent = function () { + const current = this$1.current; + + // Avoiding first `popstate` event dispatched in some browsers but first + // history route not updated since async guard at the same time. + const location = getLocation(this$1.base); + if ( + this$1.current === START && + location === this$1._startLocation + ) { + return; } - }); + + this$1.transitionTo(location, (route) => { + if (supportsScroll) { + handleScroll(router, route, current, true); + } + }); }; - window.addEventListener('popstate', handleRoutingEvent); - this.listeners.push(function () { - window.removeEventListener('popstate', handleRoutingEvent); + window.addEventListener("popstate", handleRoutingEvent); + this.listeners.push(() => { + window.removeEventListener("popstate", handleRoutingEvent); }); - }; - - HTML5History.prototype.go = function go (n) { + }; + + HTML5History.prototype.go = function go(n) { window.history.go(n); - }; - - HTML5History.prototype.push = function push (location, onComplete, onAbort) { - var this$1 = this; - - var ref = this; - var fromRoute = ref.current; - this.transitionTo(location, function (route) { - pushState(cleanPath(this$1.base + route.fullPath)); - handleScroll(this$1.router, route, fromRoute, false); - onComplete && onComplete(route); - }, onAbort); - }; - - HTML5History.prototype.replace = function replace (location, onComplete, onAbort) { - var this$1 = this; - - var ref = this; - var fromRoute = ref.current; - this.transitionTo(location, function (route) { - replaceState(cleanPath(this$1.base + route.fullPath)); - handleScroll(this$1.router, route, fromRoute, false); - onComplete && onComplete(route); - }, onAbort); - }; - - HTML5History.prototype.ensureURL = function ensureURL (push) { + }; + + HTML5History.prototype.push = function push(location, onComplete, onAbort) { + const this$1 = this; + + const ref = this; + const fromRoute = ref.current; + this.transitionTo( + location, + (route) => { + pushState(cleanPath(this$1.base + route.fullPath)); + handleScroll(this$1.router, route, fromRoute, false); + onComplete && onComplete(route); + }, + onAbort + ); + }; + + HTML5History.prototype.replace = function replace( + location, + onComplete, + onAbort + ) { + const this$1 = this; + + const ref = this; + const fromRoute = ref.current; + this.transitionTo( + location, + (route) => { + replaceState(cleanPath(this$1.base + route.fullPath)); + handleScroll(this$1.router, route, fromRoute, false); + onComplete && onComplete(route); + }, + onAbort + ); + }; + + 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.getCurrentLocation = function getCurrentLocation () { - return getLocation(this.base) - }; - - return HTML5History; - }(History)); - - function getLocation (base) { - var path = window.location.pathname; - var pathLowerCase = path.toLowerCase(); - var baseLowerCase = base.toLowerCase(); - // base="/a" shouldn't turn path="/app" into "/a/pp" - // https://github.com/vuejs/vue-router/issues/3555 - // so we ensure the trailing slash in the base - if (base && ((pathLowerCase === baseLowerCase) || - (pathLowerCase.indexOf(cleanPath(baseLowerCase + '/')) === 0))) { + const current = cleanPath(this.base + this.current.fullPath); + push ? pushState(current) : replaceState(current); + } + }; + + HTML5History.prototype.getCurrentLocation = function getCurrentLocation() { + return getLocation(this.base); + }; + + return HTML5History; +})(History); + +function getLocation(base) { + let path = window.location.pathname; + const pathLowerCase = path.toLowerCase(); + const baseLowerCase = base.toLowerCase(); + // base="/a" shouldn't turn path="/app" into "/a/pp" + // https://github.com/vuejs/vue-router/issues/3555 + // so we ensure the trailing slash in the base + if ( + base && + (pathLowerCase === baseLowerCase || + pathLowerCase.indexOf(cleanPath(`${baseLowerCase}/`)) === 0) + ) { path = path.slice(base.length); - } - return (path || '/') + window.location.search + window.location.hash - } - - /* */ - - var HashHistory = /*@__PURE__*/(function (History) { - function HashHistory (router, base, fallback) { + } + + return (path || "/") + window.location.search + window.location.hash; +} + +/* */ + +const HashHistory = /* @__PURE__*/ (function (History) { + function HashHistory(router, base, fallback) { History.call(this, router, base); // check history fallback deeplinking if (fallback && checkFallback(this.base)) { - return + return; } ensureSlash(); - } - - if ( History ) HashHistory.__proto__ = History; - HashHistory.prototype = Object.create( History && History.prototype ); - HashHistory.prototype.constructor = HashHistory; - - // this is delayed until the app mounts - // to avoid the hashchange listener being fired too early - HashHistory.prototype.setupListeners = function setupListeners () { - var this$1 = this; - + } + + if (History) HashHistory.__proto__ = History; + HashHistory.prototype = Object.create(History && History.prototype); + HashHistory.prototype.constructor = HashHistory; + + // this is delayed until the app mounts + // to avoid the hashchange listener being fired too early + HashHistory.prototype.setupListeners = function setupListeners() { + const this$1 = this; + if (this.listeners.length > 0) { - return + return; } - - var router = this.router; - var expectScroll = router.options.scrollBehavior; - var supportsScroll = supportsPushState && expectScroll; - + + const router = this.router; + const expectScroll = router.options.scrollBehavior; + const supportsScroll = supportsPushState && expectScroll; + if (supportsScroll) { - this.listeners.push(setupScroll()); - } - - var handleRoutingEvent = function () { - var current = this$1.current; - if (!ensureSlash()) { - return - } - this$1.transitionTo(getHash(), function (route) { - if (supportsScroll) { - handleScroll(this$1.router, route, current, true); - } - if (!supportsPushState) { - replaceHash(route.fullPath); + this.listeners.push(setupScroll()); + } + + const handleRoutingEvent = function () { + const current = this$1.current; + if (!ensureSlash()) { + return; } - }); + this$1.transitionTo(getHash(), (route) => { + if (supportsScroll) { + handleScroll(this$1.router, route, current, true); + } + if (!supportsPushState) { + replaceHash(route.fullPath); + } + }); }; - var eventType = supportsPushState ? 'popstate' : 'hashchange'; - window.addEventListener( - eventType, - handleRoutingEvent - ); - this.listeners.push(function () { - window.removeEventListener(eventType, handleRoutingEvent); + const eventType = supportsPushState ? "popstate" : "hashchange"; + window.addEventListener(eventType, handleRoutingEvent); + this.listeners.push(() => { + window.removeEventListener(eventType, handleRoutingEvent); }); - }; - - HashHistory.prototype.push = function push (location, onComplete, onAbort) { - var this$1 = this; - - var ref = this; - var fromRoute = ref.current; + }; + + HashHistory.prototype.push = function push(location, onComplete, onAbort) { + const this$1 = this; + + const ref = this; + const fromRoute = ref.current; this.transitionTo( - location, - function (route) { - pushHash(route.fullPath); - handleScroll(this$1.router, route, fromRoute, false); - onComplete && onComplete(route); - }, - onAbort + location, + (route) => { + pushHash(route.fullPath); + handleScroll(this$1.router, route, fromRoute, false); + onComplete && onComplete(route); + }, + onAbort ); - }; - - HashHistory.prototype.replace = function replace (location, onComplete, onAbort) { - var this$1 = this; - - var ref = this; - var fromRoute = ref.current; + }; + + HashHistory.prototype.replace = function replace( + location, + onComplete, + onAbort + ) { + const this$1 = this; + + const ref = this; + const fromRoute = ref.current; this.transitionTo( - location, - function (route) { - replaceHash(route.fullPath); - handleScroll(this$1.router, route, fromRoute, false); - onComplete && onComplete(route); - }, - onAbort + location, + (route) => { + replaceHash(route.fullPath); + handleScroll(this$1.router, route, fromRoute, false); + onComplete && onComplete(route); + }, + onAbort ); - }; - - HashHistory.prototype.go = function go (n) { + }; + + HashHistory.prototype.go = function go(n) { window.history.go(n); - }; - - HashHistory.prototype.ensureURL = function ensureURL (push) { - var current = this.current.fullPath; + }; + + HashHistory.prototype.ensureURL = function ensureURL(push) { + const current = this.current.fullPath; if (getHash() !== current) { - push ? pushHash(current) : replaceHash(current); - } - }; - - HashHistory.prototype.getCurrentLocation = function getCurrentLocation () { - return getHash() - }; - - return HashHistory; - }(History)); - - function checkFallback (base) { - var location = getLocation(base); - if (!/^\/#/.test(location)) { - window.location.replace(cleanPath(base + '/#' + location)); - return true - } - } - - 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('#'); - // empty path - if (index < 0) { return '' } - - href = href.slice(index + 1); - - return href - } - - function getUrl (path) { - var href = window.location.href; - var i = href.indexOf('#'); - var base = i >= 0 ? href.slice(0, i) : href; - return (base + "#" + path) - } - - function pushHash (path) { - if (supportsPushState) { + push ? pushHash(current) : replaceHash(current); + } + }; + + HashHistory.prototype.getCurrentLocation = function getCurrentLocation() { + return getHash(); + }; + + return HashHistory; +})(History); + +function checkFallback(base) { + const location = getLocation(base); + if (!/^\/#/.test(location)) { + window.location.replace(cleanPath(`${base}/#${location}`)); + + return true; + } +} + +function ensureSlash() { + const 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! + let href = window.location.href; + const index = href.indexOf("#"); + // empty path + if (index < 0) { + return ""; + } + + href = href.slice(index + 1); + + return href; +} + +function getUrl(path) { + const href = window.location.href; + const i = href.indexOf("#"); + const base = i >= 0 ? href.slice(0, i) : href; + + return `${base}#${path}`; +} + +function pushHash(path) { + if (supportsPushState) { pushState(getUrl(path)); - } else { + } else { window.location.hash = path; - } } - - function replaceHash (path) { - if (supportsPushState) { +} + +function replaceHash(path) { + if (supportsPushState) { replaceState(getUrl(path)); - } else { + } else { window.location.replace(getUrl(path)); - } } - - /* */ - - var AbstractHistory = /*@__PURE__*/(function (History) { - function AbstractHistory (router, base) { +} + +/* */ + +const AbstractHistory = /* @__PURE__*/ (function (History) { + function AbstractHistory(router, base) { History.call(this, router, base); 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, onComplete, onAbort) { - var this$1 = this; - + } + + if (History) AbstractHistory.__proto__ = History; + AbstractHistory.prototype = Object.create(History && History.prototype); + AbstractHistory.prototype.constructor = AbstractHistory; + + AbstractHistory.prototype.push = function push( + location, + onComplete, + onAbort + ) { + const 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++; - onComplete && onComplete(route); - }, - onAbort + location, + (route) => { + this$1.stack = this$1.stack + .slice(0, this$1.index + 1) + .concat(route); + this$1.index++; + onComplete && onComplete(route); + }, + onAbort ); - }; - - AbstractHistory.prototype.replace = function replace (location, onComplete, onAbort) { - var this$1 = this; - + }; + + AbstractHistory.prototype.replace = function replace( + location, + onComplete, + onAbort + ) { + const this$1 = this; + this.transitionTo( - location, - function (route) { - this$1.stack = this$1.stack.slice(0, this$1.index).concat(route); - onComplete && onComplete(route); - }, - onAbort + location, + (route) => { + this$1.stack = this$1.stack + .slice(0, this$1.index) + .concat(route); + onComplete && onComplete(route); + }, + onAbort ); - }; - - AbstractHistory.prototype.go = function go (n) { - var this$1 = this; - - var targetIndex = this.index + n; + }; + + AbstractHistory.prototype.go = function go(n) { + const this$1 = this; + + const targetIndex = this.index + n; if (targetIndex < 0 || targetIndex >= this.stack.length) { - return + return; } - var route = this.stack[targetIndex]; + const route = this.stack[targetIndex]; this.confirmTransition( - route, - function () { - var prev = this$1.current; - this$1.index = targetIndex; - this$1.updateRoute(route); - this$1.router.afterHooks.forEach(function (hook) { - hook && hook(route, prev); - }); - }, - function (err) { - if (isNavigationFailure(err, NavigationFailureType.duplicated)) { - this$1.index = targetIndex; + route, + () => { + const prev = this$1.current; + this$1.index = targetIndex; + this$1.updateRoute(route); + this$1.router.afterHooks.forEach((hook) => { + hook && hook(route, prev); + }); + }, + (err) => { + if ( + isNavigationFailure(err, NavigationFailureType.duplicated) + ) { + this$1.index = targetIndex; + } } - } ); - }; - - AbstractHistory.prototype.getCurrentLocation = function getCurrentLocation () { - var current = this.stack[this.stack.length - 1]; - return current ? current.fullPath : '/' - }; - - AbstractHistory.prototype.ensureURL = function ensureURL () { - // noop - }; - - return AbstractHistory; - }(History)); - - /* */ - - var VueRouter = function VueRouter (options) { - if ( options === void 0 ) options = {}; - - this.app = null; - this.apps = []; - this.options = options; - this.beforeHooks = []; - this.resolveHooks = []; - this.afterHooks = []; - this.matcher = createMatcher(options.routes || [], this); - - var mode = options.mode || 'hash'; - this.fallback = - mode === 'history' && !supportsPushState && options.fallback !== false; - 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, options.base); - break - default: - { - assert(false, ("invalid mode: " + mode)); - } - } - }; - - var prototypeAccessors = { currentRoute: { configurable: true } }; - - VueRouter.prototype.match = function match (raw, current, redirectedFrom) { - return this.matcher.match(raw, current, redirectedFrom) }; - - prototypeAccessors.currentRoute.get = function () { - return this.history && this.history.current + + AbstractHistory.prototype.getCurrentLocation = + function getCurrentLocation() { + const current = this.stack[this.stack.length - 1]; + + return current ? current.fullPath : "/"; + }; + + AbstractHistory.prototype.ensureURL = function ensureURL() { + // noop }; - - VueRouter.prototype.init = function init (app /* Vue component instance */) { - var this$1 = this; - - this.apps.push(app); - - // set up app destroyed handler - // https://github.com/vuejs/vue-router/issues/2639 - app.once('hook:destroyed', function () { + + return AbstractHistory; +})(History); + +/* */ + +const VueRouter = function VueRouter(options) { + if (options === void 0) options = {}; + + this.app = null; + this.apps = []; + this.options = options; + this.beforeHooks = []; + this.resolveHooks = []; + this.afterHooks = []; + this.matcher = createMatcher(options.routes || [], this); + + let mode = options.mode || "hash"; + this.fallback = + mode === "history" && !supportsPushState && options.fallback !== false; + 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, options.base); + break; + default: { + assert(false, `invalid mode: ${mode}`); + } + } +}; + +const prototypeAccessors = { currentRoute: { configurable: true } }; + +VueRouter.prototype.match = function match(raw, current, redirectedFrom) { + return this.matcher.match(raw, current, redirectedFrom); +}; + +prototypeAccessors.currentRoute.get = function () { + return this.history && this.history.current; +}; + +VueRouter.prototype.init = function init(app /* Vue component instance */) { + const this$1 = this; + + this.apps.push(app); + + // set up app destroyed handler + // https://github.com/vuejs/vue-router/issues/2639 + app.once("hook:destroyed", () => { // clean out app from this.apps array once destroyed - var index = this$1.apps.indexOf(app); - if (index > -1) { this$1.apps.splice(index, 1); } + const index = this$1.apps.indexOf(app); + if (index > -1) { + this$1.apps.splice(index, 1); + } // ensure we still have a main app or null if no apps // we do not release the router so it can be reused - if (this$1.app === app) { this$1.app = this$1.apps[0] || null; } - - if (!this$1.app) { this$1.history.teardown(); } - }); - - // main app previously initialized - // return as we don't need to set up new history listener - if (this.app) { - return - } - - this.app = app; - - var history = this.history; - - if (history instanceof HTML5History || history instanceof HashHistory) { - var handleInitialScroll = function (routeOrError) { - var from = history.current; - var expectScroll = this$1.options.scrollBehavior; - var supportsScroll = supportsPushState && expectScroll; - - if (supportsScroll && 'fullPath' in routeOrError) { - handleScroll(this$1, routeOrError, from, false); - } + if (this$1.app === app) { + this$1.app = this$1.apps[0] || null; + } + + if (!this$1.app) { + this$1.history.teardown(); + } + }); + + // main app previously initialized + // return as we don't need to set up new history listener + if (this.app) { + return; + } + + this.app = app; + + const history = this.history; + + if (history instanceof HTML5History || history instanceof HashHistory) { + const handleInitialScroll = function (routeOrError) { + const from = history.current; + const expectScroll = this$1.options.scrollBehavior; + const supportsScroll = supportsPushState && expectScroll; + + if (supportsScroll && "fullPath" in routeOrError) { + handleScroll(this$1, routeOrError, from, false); + } }; - var setupListeners = function (routeOrError) { - history.setupListeners(); - handleInitialScroll(routeOrError); + const setupListeners = function (routeOrError) { + history.setupListeners(); + handleInitialScroll(routeOrError); }; history.transitionTo( - history.getCurrentLocation(), - setupListeners, - setupListeners + history.getCurrentLocation(), + setupListeners, + setupListeners ); - } - - history.listen(function (route) { - this$1.apps.forEach(function (app) { - app._router.history.current = route; + } + + history.listen((route) => { + this$1.apps.forEach((app) => { + app._router.history.current = route; }); - }); - }; - - VueRouter.prototype.beforeEach = function beforeEach (fn) { - return registerHook(this.beforeHooks, fn) - }; - - VueRouter.prototype.beforeResolve = function beforeResolve (fn) { - return registerHook(this.resolveHooks, fn) - }; - - VueRouter.prototype.afterEach = function afterEach (fn) { - return registerHook(this.afterHooks, fn) - }; - - VueRouter.prototype.onReady = function onReady (cb, errorCb) { - this.history.onReady(cb, errorCb); - }; - - VueRouter.prototype.onError = function onError (errorCb) { - this.history.onError(errorCb); - }; - - VueRouter.prototype.push = function push (location, onComplete, onAbort) { - var this$1 = this; - - // $flow-disable-line - if (!onComplete && !onAbort && typeof Promise !== 'undefined') { - return new Promise(function (resolve, reject) { - this$1.history.push(location, resolve, reject); - }) - } else { + }); +}; + +VueRouter.prototype.beforeEach = function beforeEach(fn) { + return registerHook(this.beforeHooks, fn); +}; + +VueRouter.prototype.beforeResolve = function beforeResolve(fn) { + return registerHook(this.resolveHooks, fn); +}; + +VueRouter.prototype.afterEach = function afterEach(fn) { + return registerHook(this.afterHooks, fn); +}; + +VueRouter.prototype.onReady = function onReady(cb, errorCb) { + this.history.onReady(cb, errorCb); +}; + +VueRouter.prototype.onError = function onError(errorCb) { + this.history.onError(errorCb); +}; + +VueRouter.prototype.push = function push(location, onComplete, onAbort) { + const this$1 = this; + + // $flow-disable-line + if (!onComplete && !onAbort && typeof Promise !== "undefined") { + return new Promise((resolve, reject) => { + this$1.history.push(location, resolve, reject); + }); + } else { this.history.push(location, onComplete, onAbort); - } - }; - - VueRouter.prototype.replace = function replace (location, onComplete, onAbort) { - var this$1 = this; - - // $flow-disable-line - if (!onComplete && !onAbort && typeof Promise !== 'undefined') { - return new Promise(function (resolve, reject) { - this$1.history.replace(location, resolve, reject); - }) - } else { + } +}; + +VueRouter.prototype.replace = function replace(location, onComplete, onAbort) { + const this$1 = this; + + // $flow-disable-line + if (!onComplete && !onAbort && typeof Promise !== "undefined") { + return new Promise((resolve, reject) => { + this$1.history.replace(location, resolve, reject); + }); + } else { this.history.replace(location, onComplete, onAbort); - } - }; - - 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 + } +}; + +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) { + const route = to ? to.matched - ? to - : this.resolve(to).route + ? to + : this.resolve(to).route : this.currentRoute; - if (!route) { - return [] - } - return [].concat.apply( + 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 - ) { - current = current || this.history.current; - var location = normalizeLocation(to, current, append, this); - var route = this.match(location, current); - var fullPath = route.redirectedFrom || route.fullPath; - var base = this.history.base; - var href = createHref(base, fullPath, this.mode); - return { - location: location, - route: route, - href: href, + route.matched.map((m) => + Object.keys(m.components).map((key) => m.components[key]) + ) + ); +}; + +VueRouter.prototype.resolve = function resolve(to, current, append) { + current = current || this.history.current; + const location = normalizeLocation(to, current, append, this); + const route = this.match(location, current); + const fullPath = route.redirectedFrom || route.fullPath; + const base = this.history.base; + const href = createHref(base, fullPath, this.mode); + + return { + location, + route, + href, // for backwards compat normalizedTo: location, resolved: route - } - }; - - VueRouter.prototype.getRoutes = function getRoutes () { - return this.matcher.getRoutes() }; - - VueRouter.prototype.addRoute = function addRoute (parentOrRoute, route) { - this.matcher.addRoute(parentOrRoute, route); - if (this.history.current !== START) { +}; + +VueRouter.prototype.getRoutes = function getRoutes() { + return this.matcher.getRoutes(); +}; + +VueRouter.prototype.addRoute = function addRoute(parentOrRoute, route) { + this.matcher.addRoute(parentOrRoute, route); + if (this.history.current !== START) { this.history.transitionTo(this.history.getCurrentLocation()); - } + } +}; + +Object.defineProperties(VueRouter.prototype, prototypeAccessors); + +function registerHook(list, fn) { + list.push(fn); + + return function () { + const i = list.indexOf(fn); + if (i > -1) { + list.splice(i, 1); + } }; - - Object.defineProperties( VueRouter.prototype, prototypeAccessors ); - - function registerHook (list, fn) { - list.push(fn); - return function () { - var i = list.indexOf(fn); - if (i > -1) { list.splice(i, 1); } - } - } - - function createHref (base, fullPath, mode) { - var path = mode === 'hash' ? '#' + fullPath : fullPath; - return base ? cleanPath(base + '/' + path) : path - } - - // VueRouter.install = install; - VueRouter.version = '3.5.2'; - VueRouter.isNavigationFailure = isNavigationFailure; - VueRouter.NavigationFailureType = NavigationFailureType; - VueRouter.START_LOCATION = START; - - - var $router, cbs = []; - BI.RouterWidget = BI.inherit(BI.Widget, { - init: function () { - this.$router = this._router = BI.Router.$router = $router = new VueRouter({ - mode: this.options.mode, - routes: this.options.routes, - base: this.options.base, - }); - Fix.defineReactiveProperty(BI.Router.$router.history, "current"); - this.$router.beforeEach(function (to, from, next) { - if (to.matched.length === 0) { - //如果上级也未匹配到路由则跳转主页面,如果上级能匹配到则转上级路由 - from.path ? next({ path: from.path }) : next('/'); - } else { - //如果匹配到正确跳转 - next(); - } +} + +function createHref(base, fullPath, mode) { + const path = mode === "hash" ? `#${fullPath}` : fullPath; + + return base ? cleanPath(`${base}/${path}`) : path; +} + +// VueRouter.install = install; +VueRouter.version = "3.5.2"; +VueRouter.isNavigationFailure = isNavigationFailure; +VueRouter.NavigationFailureType = NavigationFailureType; +VueRouter.START_LOCATION = START; + +let $router; +const cbs = []; +@shortcut() +export class RouterWidget extends Widget { + static xtype = "bi.router"; + + init() { + this.$router = + this._router = + Router.$router = + $router = + new VueRouter({ + mode: this.options.mode, + routes: this.options.routes, + base: this.options.base + }); + Fix.defineReactiveProperty(Router.$router.history, "current"); + this.$router.beforeEach((to, from, next) => { + if (to.matched.length === 0) { + // 如果上级也未匹配到路由则跳转主页面,如果上级能匹配到则转上级路由 + from.path ? next({ path: from.path }) : next("/"); + } else { + // 如果匹配到正确跳转 + next(); + } }); - this.$router.afterEach(function () { - cbs.forEach(function (cb) {cb();}); + this.$router.afterEach(() => { + cbs.forEach((cb) => { + cb(); + }); }); this.$router.init(this); - } - }); - BI.shortcut("bi.router", BI.RouterWidget); - - BI.RouterView = BI.inherit(BI.Widget, { - props: { - baseCls: 'bi-router-view', + } +} + +@shortcut() +export class RouterView extends Widget { + static xtype = "bi.router_view"; + + props = { + baseCls: "bi-router-view", deps: 0, - name: 'default' - }, - created: function () { - var self = this, o = this.options; - cbs.push(this._callbackListener = function () { - var current = $router.history.current; - // 匹配的路径名(/component/:id) - var matchedPath = current.matched[o.deps] && current.matched[o.deps].path; - var component = current.matched[o.deps] && current.matched[o.deps].components[o.name]; - - if (BI.isNotNull(component)) { - if (matchedPath) { - BI.each(current.params, function (key, value) { - // 把 :id 替换成具体的值(/component/demo.td) - matchedPath = matchedPath.replace(`:${key}`, value); - }); - } - self.tab.setSelect(matchedPath || "/"); - } - }); + name: "default" + }; + created() { + const self = this, + o = this.options; + cbs.push( + (this._callbackListener = function () { + const current = $router.history.current; + // 匹配的路径名(/component/:id) + let matchedPath = + current.matched[o.deps] && current.matched[o.deps].path; + const component = + current.matched[o.deps] && + current.matched[o.deps].components[o.name]; + + if (BI.isNotNull(component)) { + if (matchedPath) { + BI.each(current.params, (key, value) => { + // 把 :id 替换成具体的值(/component/demo.td) + matchedPath = matchedPath.replace(`:${key}`, value); + }); + } + self.tab.setSelect(matchedPath || "/"); + } + }) + ); // "bi.router_view"是由"bi.tab"实现的,cardCreator是一个异步过程,在"bi.router_view"创建之前,cbs里不会有创建子组件的方法,在初始化路由时,没法直接渲染到子组件,所以这里手动加了一次调用 this._callbackListener(); - }, - render: function () { - var self = this, o = this.options; + } + + render() { + const self = this, + o = this.options; + return { - type: "bi.tab", - ref: function (_ref) { - self.tab = _ref; - }, - single: o.single, // 是不是单页面 - keepAlives: o.keepAlives, - logic: { - dynamic: false - }, - showIndex: false, - cardCreator: function (v) { - return $router.history.current.matched[o.deps].components[o.name]; - } + type: "bi.tab", + ref(_ref) { + self.tab = _ref; + }, + single: o.single, // 是不是单页面 + keepAlives: o.keepAlives, + logic: { + dynamic: false + }, + showIndex: false, + cardCreator(v) { + return $router.history.current.matched[o.deps].components[ + o.name + ]; + } }; - }, - destroyed: function () { + } + + destroyed() { // BI.remove方法会把第二个参数当迭代器执行导致方法多执行一遍 cbs.splice(cbs.indexOf(this._callbackListener), 1); - } - }); - BI.shortcut("bi.router_view", BI.RouterView); - - BI.Router = BI.Router || VueRouter; - BI.Router.isSameRoute = isSameRoute; - return VueRouter; - - }))); - \ No newline at end of file + } +} + +Router.isSameRoute = isSameRoute; diff --git a/src/widget/index.js b/src/widget/index.js index 399297233..2cdd59ea2 100644 --- a/src/widget/index.js +++ b/src/widget/index.js @@ -1,4 +1,5 @@ export { Collapse } from "./collapse/collapse"; +export { SelectTreeExpander } from "./selecttree/selecttree.expander"; export { SelectTreeCombo } from "./selecttree/selecttree.combo"; export { SingleTreeCombo } from "./singletree/singletree.combo"; export { MultiTreeCombo } from "./multitree/multi.tree.combo"; diff --git a/template/index.html b/template/index.html index bcf5ad41a..f37fddb06 100644 --- a/template/index.html +++ b/template/index.html @@ -38,7 +38,7 @@
- +