commit fbc3a70cdaf1433cee65867bc97b4f5fcd139585 Author: walker Date: Sat May 6 16:28:04 2023 +0800 图形应用框架,包含组件库示例和应用示例 diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9d08a1a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..7b59e09 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,7 @@ +/dist +/src-capacitor +/src-cordova +/.quasar +/node_modules +.eslintrc.js +/src-ssr diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..765228b --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,89 @@ +module.exports = { + // https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy + // This option interrupts the configuration hierarchy at this file + // Remove this if you have an higher level ESLint config file (it usually happens into a monorepos) + root: true, + + // https://eslint.vuejs.org/user-guide/#how-to-use-a-custom-parser + // Must use parserOptions instead of "parser" to allow vue-eslint-parser to keep working + // `parser: 'vue-eslint-parser'` is already included with any 'plugin:vue/**' config and should be omitted + parserOptions: { + parser: require.resolve('@typescript-eslint/parser'), + extraFileExtensions: ['.vue'], + }, + + env: { + browser: true, + es2021: true, + node: true, + 'vue/setup-compiler-macros': true, + }, + + // Rules order is important, please avoid shuffling them + extends: [ + // Base ESLint recommended rules + // 'eslint:recommended', + + // https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#usage + // ESLint typescript rules + 'plugin:@typescript-eslint/recommended', + + // Uncomment any of the lines below to choose desired strictness, + // but leave only one uncommented! + // See https://eslint.vuejs.org/rules/#available-rules + 'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention) + // 'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability) + // 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead) + + // https://github.com/prettier/eslint-config-prettier#installation + // usage with Prettier, provided by 'eslint-config-prettier'. + 'prettier', + ], + + plugins: [ + // required to apply rules which need type information + '@typescript-eslint', + + // https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files + // required to lint *.vue files + 'vue', + + // https://github.com/typescript-eslint/typescript-eslint/issues/389#issuecomment-509292674 + // Prettier has not been included as plugin to avoid performance impact + // add it as an extension for your IDE + ], + + globals: { + ga: 'readonly', // Google Analytics + cordova: 'readonly', + __statics: 'readonly', + __QUASAR_SSR__: 'readonly', + __QUASAR_SSR_SERVER__: 'readonly', + __QUASAR_SSR_CLIENT__: 'readonly', + __QUASAR_SSR_PWA__: 'readonly', + process: 'readonly', + Capacitor: 'readonly', + chrome: 'readonly', + }, + + // add your custom rules here + rules: { + 'prefer-promise-reject-errors': 'off', + + quotes: ['warn', 'single', { avoidEscape: true }], + + // this rule, if on, would require explicit return type on the `render` function + '@typescript-eslint/explicit-function-return-type': 'off', + + // in plain CommonJS modules, you can't use `import foo = require('foo')` to pass this rule, so it has to be disabled + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-namespace': 'off', + + // The core 'no-unused-vars' rules (in the eslint:recommended ruleset) + // does not work with type definitions + 'no-unused-vars': 'off', + + // allow debugger during development only + 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', + }, +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..553e134 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +.DS_Store +.thumbs.db +node_modules + +# Quasar core related directories +.quasar +/dist + +# Cordova related directories and files +/src-cordova/node_modules +/src-cordova/platforms +/src-cordova/plugins +/src-cordova/www + +# Capacitor related directories and files +/src-capacitor/www +/src-capacitor/node_modules + +# BEX related directories and files +/src-bex/www +/src-bex/js/core + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..32bd84d --- /dev/null +++ b/.npmrc @@ -0,0 +1,3 @@ +# pnpm-related options +shamefully-hoist=true +strict-peer-dependencies=false diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..650cb88 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "semi": true +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..fe38802 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,15 @@ +{ + "recommendations": [ + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "editorconfig.editorconfig", + "vue.volar", + "wayou.vscode-todo-highlight" + ], + "unwantedRecommendations": [ + "octref.vetur", + "hookyqr.beautify", + "dbaeumer.jshint", + "ms-vscode.vscode-typescript-tslint-plugin" + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..746cf57 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,16 @@ +{ + "editor.bracketPairColorization.enabled": true, + "editor.guides.bracketPairs": true, + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.codeActionsOnSave": [ + "source.fixAll.eslint" + ], + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "vue" + ], + "typescript.tsdk": "node_modules/typescript/lib" +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..edd6a13 --- /dev/null +++ b/README.md @@ -0,0 +1,73 @@ +# 项目说明 + +图形应用基础框架,基于 pixi.js([官网](https://pixijs.com/), [API Docs](https://pixijs.download/release/docs/index.html)) +viewport 使用的 github 开源的 pixi-viewport[pixi-viewport](https://github.com/davidfig/pixi-viewport) + +# 路线图 + +- 图形的位置、旋转属性使用 pixijs 的 transform 变换(完成) +- 图形对象的拖拽使用原始的 transform(图形的变换使用原始的变换)(完成) +- 图形交互抽象(完成) +- 图形子元素变换处理和存储(完成) +- 图形复制功能(完成) +- 绘制应用图形外包围框及旋转缩放功能(完成) +- 绘制增加吸附功能(移动到特定位置附近吸附)(完成) +- 菜单事件及处理 +- 打包 +- 添加拖拽轨迹限制功能 +- 添加图形对象 可编辑属性 定义功能 + +# pixijs 一些概念 + +- 如果图形对象本身有碰撞检测区域,子图形的碰撞检测范围在父级的碰撞检测区域内 +- v7.2 中 eventMode 属性值的意义: + + > - "none":忽略所有交互事件,且忽略其子级的交互事件。 + + > - "passive":不发出(emit)事件,并忽略对自身和非交互式子对象的所有命中测试。交互式子项仍将发出(emit)事件。 + + > - "auto":不发出(emit)事件,但如果父级是交互式的,则会进行命中测试。与 v7 中的 interactive=false 相同 + + > - "static":发出(emit)事件并进行命中测试。与 v7 中的 interactive=true 相同 + + > - "dynamic":发出(emit)事件并进行命中测试,但也会接收模拟的交互事件,以便在鼠标不移动时进行交互 + +## Install the dependencies + +```bash +yarn +# or +npm install +``` + +### Start the app in development mode (hot-code reloading, error reporting, etc.) + +```bash +quasar dev +``` + +### Lint the files + +```bash +yarn lint +# or +npm run lint +``` + +### Format the files + +```bash +yarn format +# or +npm run format +``` + +### Build the app for production + +```bash +quasar build +``` + +### Customize the configuration + +See [Configuring quasar.config.js](https://v2.quasar.dev/quasar-cli-vite/quasar-config-js). diff --git a/index.html b/index.html new file mode 100644 index 0000000..3c8c78f --- /dev/null +++ b/index.html @@ -0,0 +1,21 @@ + + + + <%= productName %> + + + + + + + + + + + + + + + + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..0e5ecc8 --- /dev/null +++ b/package.json @@ -0,0 +1,46 @@ +{ + "name": "graphic-pixi", + "version": "0.0.1", + "description": "基于pixijs的图形应用、绘制应用框架", + "productName": "Graphic-pixi", + "author": "walker ", + "private": true, + "scripts": { + "lint": "eslint --ext .js,.ts,.vue ./", + "format": "prettier --write \"**/*.{js,ts,vue,scss,html,md,json}\" --ignore-path .gitignore", + "test": "echo \"No test specified\" && exit 0", + "dev": "quasar dev", + "proto": "node scripts/proto.cjs", + "build": "quasar build" + }, + "dependencies": { + "@quasar/extras": "^1.0.0", + "@stomp/stompjs": "^7.0.0", + "google-protobuf": "^3.21.2", + "js-base64": "^3.7.5", + "pinia": "^2.0.11", + "pixi-viewport": "^5.0.1", + "pixi.js": "^7.2.4", + "quasar": "^2.6.0", + "vue": "^3.0.0", + "vue-router": "^4.0.0" + }, + "devDependencies": { + "@quasar/app-vite": "^1.0.0", + "@types/google-protobuf": "^3.15.6", + "@types/node": "^12.20.21", + "@typescript-eslint/eslint-plugin": "^5.10.0", + "@typescript-eslint/parser": "^5.10.0", + "autoprefixer": "^10.4.2", + "eslint": "^8.10.0", + "eslint-config-prettier": "^8.1.0", + "eslint-plugin-vue": "^9.0.0", + "prettier": "^2.5.1", + "typescript": "^4.5.4" + }, + "engines": { + "node": "^18 || ^16 || ^14.19", + "npm": ">= 6.13.4", + "yarn": ">= 1.21.1" + } +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..94b7b1c --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,27 @@ +/* eslint-disable */ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + plugins: [ + // https://github.com/postcss/autoprefixer + require('autoprefixer')({ + overrideBrowserslist: [ + 'last 4 Chrome versions', + 'last 4 Firefox versions', + 'last 4 Edge versions', + 'last 4 Safari versions', + 'last 4 Android versions', + 'last 4 ChromeAndroid versions', + 'last 4 FirefoxAndroid versions', + 'last 4 iOS versions' + ] + }) + + // https://github.com/elchininet/postcss-rtlcss + // If you want to support RTL css, then + // 1. yarn/npm install postcss-rtlcss + // 2. optionally set quasar.config.js > framework > lang to an RTL language + // 3. uncomment the following line: + // require('postcss-rtlcss') + ] +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..ae7bbdb Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/icons/favicon-128x128.png b/public/icons/favicon-128x128.png new file mode 100644 index 0000000..1401176 Binary files /dev/null and b/public/icons/favicon-128x128.png differ diff --git a/public/icons/favicon-16x16.png b/public/icons/favicon-16x16.png new file mode 100644 index 0000000..679063a Binary files /dev/null and b/public/icons/favicon-16x16.png differ diff --git a/public/icons/favicon-32x32.png b/public/icons/favicon-32x32.png new file mode 100644 index 0000000..fd1fbc6 Binary files /dev/null and b/public/icons/favicon-32x32.png differ diff --git a/public/icons/favicon-96x96.png b/public/icons/favicon-96x96.png new file mode 100644 index 0000000..e93b80a Binary files /dev/null and b/public/icons/favicon-96x96.png differ diff --git a/quasar.config.js b/quasar.config.js new file mode 100644 index 0000000..6d7aa33 --- /dev/null +++ b/quasar.config.js @@ -0,0 +1,208 @@ +/* eslint-env node */ + +/* + * This file runs in a Node context (it's NOT transpiled by Babel), so use only + * the ES6 features that are supported by your Node version. https://node.green/ + */ + +// Configuration for your app +// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js + +const { configure } = require('quasar/wrappers'); + +module.exports = configure(function (/* ctx */) { + return { + eslint: { + // fix: true, + // include: [], + // exclude: [], + // rawOptions: {}, + warnings: true, + errors: true, + exclude: ['src/examples/app/protos/*'], + }, + + // https://v2.quasar.dev/quasar-cli-vite/prefetch-feature + // preFetch: true, + + // app boot file (/src/boot) + // --> boot files are part of "main.js" + // https://v2.quasar.dev/quasar-cli-vite/boot-files + boot: [], + + // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css + css: ['app.scss'], + + // https://github.com/quasarframework/quasar/tree/dev/extras + extras: [ + // 'ionicons-v4', + // 'mdi-v5', + // 'fontawesome-v6', + // 'eva-icons', + // 'themify', + // 'line-awesome', + // 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both! + + 'roboto-font', // optional, you are not bound to it + 'material-icons', // optional, you are not bound to it + ], + + // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build + build: { + target: { + browser: ['es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1'], + node: 'node16', + }, + + vueRouterMode: 'history', // available values: 'hash', 'history' + // vueRouterBase, + // vueDevtools, + // vueOptionsAPI: false, + + // rebuildCache: true, // rebuilds Vite/linter/etc cache on startup + + // publicPath: '/', + // analyze: true, + // env: {}, + // rawDefine: {} + // ignorePublicFolder: true, + // minify: false, + // polyfillModulePreload: true, + // distDir + + // extendViteConf (viteConf) {}, + // viteVuePluginOptions: {}, + + // vitePlugins: [ + // [ 'package-name', { ..options.. } ] + // ] + }, + + // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer + devServer: { + // https: true + port: 9999, + open: false, // opens browser window automatically + }, + + // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework + framework: { + config: { + notify: { + position: 'top', + timeout: 2000, + progress: true, + }, + }, + + // iconSet: 'material-icons', // Quasar icon set + lang: 'zh-CN', // Quasar language pack + + // For special cases outside of where the auto-import strategy can have an impact + // (like functional components as one of the examples), + // you can manually specify Quasar components/directives to be available everywhere: + // + // components: [], + // directives: [], + + // Quasar plugins + plugins: ['Notify', 'Dialog', 'Dark', 'AppFullscreen'], + }, + + // animations: 'all', // --- includes all animations + // https://v2.quasar.dev/options/animations + animations: [], + + // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#sourcefiles + // sourceFiles: { + // rootComponent: 'src/App.vue', + // router: 'src/router/index', + // store: 'src/store/index', + // registerServiceWorker: 'src-pwa/register-service-worker', + // serviceWorker: 'src-pwa/custom-service-worker', + // pwaManifestFile: 'src-pwa/manifest.json', + // electronMain: 'src-electron/electron-main', + // electronPreload: 'src-electron/electron-preload' + // }, + + // https://v2.quasar.dev/quasar-cli-vite/developing-ssr/configuring-ssr + ssr: { + // ssrPwaHtmlFilename: 'offline.html', // do NOT use index.html as name! + // will mess up SSR + + // extendSSRWebserverConf (esbuildConf) {}, + // extendPackageJson (json) {}, + + pwa: false, + + // manualStoreHydration: true, + // manualPostHydrationTrigger: true, + + prodPort: 3000, // The default port that the production server should use + // (gets superseded if process.env.PORT is specified at runtime) + + middlewares: [ + 'render', // keep this as last one + ], + }, + + // https://v2.quasar.dev/quasar-cli-vite/developing-pwa/configuring-pwa + pwa: { + workboxMode: 'generateSW', // or 'injectManifest' + injectPwaMetaTags: true, + swFilename: 'sw.js', + manifestFilename: 'manifest.json', + useCredentialsForManifestTag: false, + // useFilenameHashes: true, + // extendGenerateSWOptions (cfg) {} + // extendInjectManifestOptions (cfg) {}, + // extendManifestJson (json) {} + // extendPWACustomSWConf (esbuildConf) {} + }, + + // Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-cordova-apps/configuring-cordova + cordova: { + // noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing + }, + + // Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-capacitor-apps/configuring-capacitor + capacitor: { + hideSplashscreen: true, + }, + + // Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-electron-apps/configuring-electron + electron: { + // extendElectronMainConf (esbuildConf) + // extendElectronPreloadConf (esbuildConf) + + inspectPort: 5858, + + bundler: 'packager', // 'packager' or 'builder' + + packager: { + // https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options + // OS X / Mac App Store + // appBundleId: '', + // appCategoryType: '', + // osxSign: '', + // protocol: 'myapp://path', + // Windows only + // win32metadata: { ... } + }, + + builder: { + // https://www.electron.build/configuration/configuration + + appId: 'graphic-pixi', + }, + }, + + // Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-browser-extensions/configuring-bex + bex: { + contentScripts: ['my-content-script'], + + // extendBexScriptsConf (esbuildConf) {} + // extendBexManifestJson (json) {} + }, + }; +}); diff --git a/scripts/proto.cjs b/scripts/proto.cjs new file mode 100644 index 0000000..67ef276 --- /dev/null +++ b/scripts/proto.cjs @@ -0,0 +1,95 @@ +/** + * 将proto文件编译到 src/proto/ + */ +const { readdirSync } = require('fs'); +const { resolve } = require('path'); +const os = require('os'); + +const { exec } = require('child_process'); + +const protocDir = resolve( + __dirname, + '../src/examples/app/app_message/protoc-22.2' +); +const protoFileDir = resolve( + __dirname, + '../src/examples/app/app_message/protos' +); +const destDir = resolve(__dirname, '../src/examples/app/protos'); + +/** + * 递归处理所有proto文件生成 + * @param {*} file 文件 + * @param {*} path 目录 + */ +function recursiveGenerate(file, path = [], cmds = []) { + if (file.isFile()) { + // 文件,生成 + if (file.name.endsWith('.proto')) { + cmds.push(buildGenerateCmd(file.name, path)); + } else { + console.warn('不是proto文件', file.name); + } + } else if (file.isDirectory()) { + // 文件夹,递归 + readdirSync(resolve(protoFileDir, ...path, file.name), { + withFileTypes: true, + }).forEach((f) => { + const subPath = [...path, file.name]; + recursiveGenerate(f, subPath, cmds); + }); + } +} + +const isLinux = os.type().toLowerCase().includes('linux'); + +function buildGenerateCmd(name, path = []) { + const protoPath = resolve(protoFileDir, ...path); + const tsPath = resolve(destDir, ...path); + + let cmd = ['protoc', `-I=${protoPath}`, `--ts_out=${tsPath}`, `${name}`]; + let cmdStr = cmd.join(' '); + return cmdStr; +} + +function main() { + const protocBin = resolve( + protocDir, + `bin/${isLinux ? 'linux-x86_64' : 'win64'}` + ); + const prepareCmds = []; + const setPathCmd = isLinux + ? ['export', `PATH=${protocBin}:${protocDir}:"$PATH"`].join(' ') + : ['set', `PATH=${protocBin};${protocDir};%PATH%`].join(' '); + const protocVersionCmd = ['protoc', '--version'].join(' '); + prepareCmds.push(setPathCmd); + prepareCmds.push(protocVersionCmd); + + readdirSync(protoFileDir, { + withFileTypes: true, + }).forEach((f) => { + recursiveGenerate(f, [], prepareCmds); + }); + + console.log(prepareCmds); + + exec( + prepareCmds.join(' && '), + { + maxBuffer: 1024 * 2000, + }, + (err, stdout, stderr) => { + if (err) { + console.error(err); + throw err; + } else if (stderr.length > 0) { + console.error(stderr.toString()); + throw new Error(stderr.toString()); + } else { + console.log(stdout); + } + } + ); +} + +main(); diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..fe0a246 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,7 @@ + + + diff --git a/src/app/index.ts b/src/app/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/assets/quasar-logo-vertical.svg b/src/assets/quasar-logo-vertical.svg new file mode 100644 index 0000000..8210831 --- /dev/null +++ b/src/assets/quasar-logo-vertical.svg @@ -0,0 +1,15 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/boot/.gitkeep b/src/boot/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/components/EssentialLink.vue b/src/components/EssentialLink.vue new file mode 100644 index 0000000..13205c2 --- /dev/null +++ b/src/components/EssentialLink.vue @@ -0,0 +1,34 @@ + + + diff --git a/src/components/ExampleComponent.vue b/src/components/ExampleComponent.vue new file mode 100644 index 0000000..bc03c3d --- /dev/null +++ b/src/components/ExampleComponent.vue @@ -0,0 +1,37 @@ + + + diff --git a/src/components/models.ts b/src/components/models.ts new file mode 100644 index 0000000..6945920 --- /dev/null +++ b/src/components/models.ts @@ -0,0 +1,8 @@ +export interface Todo { + id: number; + content: string; +} + +export interface Meta { + totalCount: number; +} diff --git a/src/css/app.scss b/src/css/app.scss new file mode 100644 index 0000000..ecac98f --- /dev/null +++ b/src/css/app.scss @@ -0,0 +1 @@ +// app global css in SCSS form diff --git a/src/css/quasar.variables.scss b/src/css/quasar.variables.scss new file mode 100644 index 0000000..3996ce1 --- /dev/null +++ b/src/css/quasar.variables.scss @@ -0,0 +1,25 @@ +// Quasar SCSS (& Sass) Variables +// -------------------------------------------------- +// To customize the look and feel of this app, you can override +// the Sass/SCSS variables found in Quasar's source Sass/SCSS files. + +// Check documentation for full list of Quasar variables + +// Your own variables (that are declared here) and Quasar's own +// ones will be available out of the box in your .vue/.scss/.sass files + +// It's highly recommended to change the default colors +// to match your app's branding. +// Tip: Use the "Theme Builder" on Quasar's documentation website. + +$primary : #1976D2; +$secondary : #26A69A; +$accent : #9C27B0; + +$dark : #1D1D1D; +$dark-page : #121212; + +$positive : #21BA45; +$negative : #C10015; +$info : #31CCEC; +$warning : #F2C037; diff --git a/src/env.d.ts b/src/env.d.ts new file mode 100644 index 0000000..dd757b1 --- /dev/null +++ b/src/env.d.ts @@ -0,0 +1,9 @@ +/* eslint-disable */ + +declare namespace NodeJS { + interface ProcessEnv { + NODE_ENV: string; + VUE_ROUTER_MODE: 'hash' | 'history' | 'abstract' | undefined; + VUE_ROUTER_BASE: string | undefined; + } +} diff --git a/src/examples/app/app_message/protoc-22.2/bin/linux-x86_64/protoc b/src/examples/app/app_message/protoc-22.2/bin/linux-x86_64/protoc new file mode 100644 index 0000000..c74fab1 Binary files /dev/null and b/src/examples/app/app_message/protoc-22.2/bin/linux-x86_64/protoc differ diff --git a/src/examples/app/app_message/protoc-22.2/bin/win64/protoc.exe b/src/examples/app/app_message/protoc-22.2/bin/win64/protoc.exe new file mode 100644 index 0000000..149389c Binary files /dev/null and b/src/examples/app/app_message/protoc-22.2/bin/win64/protoc.exe differ diff --git a/src/examples/app/app_message/protoc-22.2/include/google/protobuf/any.proto b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/any.proto new file mode 100644 index 0000000..561da0c --- /dev/null +++ b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/any.proto @@ -0,0 +1,161 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option go_package = "google.golang.org/protobuf/types/known/anypb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "AnyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; + +// `Any` contains an arbitrary serialized protocol buffer message along with a +// URL that describes the type of the serialized message. +// +// Protobuf library provides support to pack/unpack Any values in the form +// of utility functions or additional generated methods of the Any type. +// +// Example 1: Pack and unpack a message in C++. +// +// Foo foo = ...; +// Any any; +// any.PackFrom(foo); +// ... +// if (any.UnpackTo(&foo)) { +// ... +// } +// +// Example 2: Pack and unpack a message in Java. +// +// Foo foo = ...; +// Any any = Any.pack(foo); +// ... +// if (any.is(Foo.class)) { +// foo = any.unpack(Foo.class); +// } +// // or ... +// if (any.isSameTypeAs(Foo.getDefaultInstance())) { +// foo = any.unpack(Foo.getDefaultInstance()); +// } +// +// Example 3: Pack and unpack a message in Python. +// +// foo = Foo(...) +// any = Any() +// any.Pack(foo) +// ... +// if any.Is(Foo.DESCRIPTOR): +// any.Unpack(foo) +// ... +// +// Example 4: Pack and unpack a message in Go +// +// foo := &pb.Foo{...} +// any, err := anypb.New(foo) +// if err != nil { +// ... +// } +// ... +// foo := &pb.Foo{} +// if err := any.UnmarshalTo(foo); err != nil { +// ... +// } +// +// The pack methods provided by protobuf library will by default use +// 'type.googleapis.com/full.type.name' as the type URL and the unpack +// methods only use the fully qualified type name after the last '/' +// in the type URL, for example "foo.bar.com/x/y.z" will yield type +// name "y.z". +// +// JSON +// +// The JSON representation of an `Any` value uses the regular +// representation of the deserialized, embedded message, with an +// additional field `@type` which contains the type URL. Example: +// +// package google.profile; +// message Person { +// string first_name = 1; +// string last_name = 2; +// } +// +// { +// "@type": "type.googleapis.com/google.profile.Person", +// "firstName": , +// "lastName": +// } +// +// If the embedded message type is well-known and has a custom JSON +// representation, that representation will be embedded adding a field +// `value` which holds the custom JSON in addition to the `@type` +// field. Example (for message [google.protobuf.Duration][]): +// +// { +// "@type": "type.googleapis.com/google.protobuf.Duration", +// "value": "1.212s" +// } +// +message Any { + // A URL/resource name that uniquely identifies the type of the serialized + // protocol buffer message. This string must contain at least + // one "/" character. The last segment of the URL's path must represent + // the fully qualified name of the type (as in + // `path/google.protobuf.Duration`). The name should be in a canonical form + // (e.g., leading "." is not accepted). + // + // In practice, teams usually precompile into the binary all types that they + // expect it to use in the context of Any. However, for URLs which use the + // scheme `http`, `https`, or no scheme, one can optionally set up a type + // server that maps type URLs to message definitions as follows: + // + // * If no scheme is provided, `https` is assumed. + // * An HTTP GET on the URL must yield a [google.protobuf.Type][] + // value in binary format, or produce an error. + // * Applications are allowed to cache lookup results based on the + // URL, or have them precompiled into a binary to avoid any + // lookup. Therefore, binary compatibility needs to be preserved + // on changes to types. (Use versioned type names to manage + // breaking changes.) + // + // Note: this functionality is not currently available in the official + // protobuf release, and it is not used for type URLs beginning with + // type.googleapis.com. + // + // Schemes other than `http`, `https` (or the empty scheme) might be + // used with implementation specific semantics. + // + string type_url = 1; + + // Must be a valid serialized protocol buffer of the above specified type. + bytes value = 2; +} diff --git a/src/examples/app/app_message/protoc-22.2/include/google/protobuf/api.proto b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/api.proto new file mode 100644 index 0000000..afc9cc1 --- /dev/null +++ b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/api.proto @@ -0,0 +1,207 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +import "google/protobuf/source_context.proto"; +import "google/protobuf/type.proto"; + +option java_package = "com.google.protobuf"; +option java_outer_classname = "ApiProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "google.golang.org/protobuf/types/known/apipb"; + +// Api is a light-weight descriptor for an API Interface. +// +// Interfaces are also described as "protocol buffer services" in some contexts, +// such as by the "service" keyword in a .proto file, but they are different +// from API Services, which represent a concrete implementation of an interface +// as opposed to simply a description of methods and bindings. They are also +// sometimes simply referred to as "APIs" in other contexts, such as the name of +// this message itself. See https://cloud.google.com/apis/design/glossary for +// detailed terminology. +message Api { + // The fully qualified name of this interface, including package name + // followed by the interface's simple name. + string name = 1; + + // The methods of this interface, in unspecified order. + repeated Method methods = 2; + + // Any metadata attached to the interface. + repeated Option options = 3; + + // A version string for this interface. If specified, must have the form + // `major-version.minor-version`, as in `1.10`. If the minor version is + // omitted, it defaults to zero. If the entire version field is empty, the + // major version is derived from the package name, as outlined below. If the + // field is not empty, the version in the package name will be verified to be + // consistent with what is provided here. + // + // The versioning schema uses [semantic + // versioning](http://semver.org) where the major version number + // indicates a breaking change and the minor version an additive, + // non-breaking change. Both version numbers are signals to users + // what to expect from different versions, and should be carefully + // chosen based on the product plan. + // + // The major version is also reflected in the package name of the + // interface, which must end in `v`, as in + // `google.feature.v1`. For major versions 0 and 1, the suffix can + // be omitted. Zero major versions must only be used for + // experimental, non-GA interfaces. + // + string version = 4; + + // Source context for the protocol buffer service represented by this + // message. + SourceContext source_context = 5; + + // Included interfaces. See [Mixin][]. + repeated Mixin mixins = 6; + + // The source syntax of the service. + Syntax syntax = 7; +} + +// Method represents a method of an API interface. +message Method { + // The simple name of this method. + string name = 1; + + // A URL of the input message type. + string request_type_url = 2; + + // If true, the request is streamed. + bool request_streaming = 3; + + // The URL of the output message type. + string response_type_url = 4; + + // If true, the response is streamed. + bool response_streaming = 5; + + // Any metadata attached to the method. + repeated Option options = 6; + + // The source syntax of this method. + Syntax syntax = 7; +} + +// Declares an API Interface to be included in this interface. The including +// interface must redeclare all the methods from the included interface, but +// documentation and options are inherited as follows: +// +// - If after comment and whitespace stripping, the documentation +// string of the redeclared method is empty, it will be inherited +// from the original method. +// +// - Each annotation belonging to the service config (http, +// visibility) which is not set in the redeclared method will be +// inherited. +// +// - If an http annotation is inherited, the path pattern will be +// modified as follows. Any version prefix will be replaced by the +// version of the including interface plus the [root][] path if +// specified. +// +// Example of a simple mixin: +// +// package google.acl.v1; +// service AccessControl { +// // Get the underlying ACL object. +// rpc GetAcl(GetAclRequest) returns (Acl) { +// option (google.api.http).get = "/v1/{resource=**}:getAcl"; +// } +// } +// +// package google.storage.v2; +// service Storage { +// rpc GetAcl(GetAclRequest) returns (Acl); +// +// // Get a data record. +// rpc GetData(GetDataRequest) returns (Data) { +// option (google.api.http).get = "/v2/{resource=**}"; +// } +// } +// +// Example of a mixin configuration: +// +// apis: +// - name: google.storage.v2.Storage +// mixins: +// - name: google.acl.v1.AccessControl +// +// The mixin construct implies that all methods in `AccessControl` are +// also declared with same name and request/response types in +// `Storage`. A documentation generator or annotation processor will +// see the effective `Storage.GetAcl` method after inheriting +// documentation and annotations as follows: +// +// service Storage { +// // Get the underlying ACL object. +// rpc GetAcl(GetAclRequest) returns (Acl) { +// option (google.api.http).get = "/v2/{resource=**}:getAcl"; +// } +// ... +// } +// +// Note how the version in the path pattern changed from `v1` to `v2`. +// +// If the `root` field in the mixin is specified, it should be a +// relative path under which inherited HTTP paths are placed. Example: +// +// apis: +// - name: google.storage.v2.Storage +// mixins: +// - name: google.acl.v1.AccessControl +// root: acls +// +// This implies the following inherited HTTP annotation: +// +// service Storage { +// // Get the underlying ACL object. +// rpc GetAcl(GetAclRequest) returns (Acl) { +// option (google.api.http).get = "/v2/acls/{resource=**}:getAcl"; +// } +// ... +// } +message Mixin { + // The fully qualified name of the interface which is included. + string name = 1; + + // If non-empty specifies a path under which inherited HTTP paths + // are rooted. + string root = 2; +} diff --git a/src/examples/app/app_message/protoc-22.2/include/google/protobuf/compiler/plugin.proto b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/compiler/plugin.proto new file mode 100644 index 0000000..7ab6dff --- /dev/null +++ b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/compiler/plugin.proto @@ -0,0 +1,180 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// +// protoc (aka the Protocol Compiler) can be extended via plugins. A plugin is +// just a program that reads a CodeGeneratorRequest from stdin and writes a +// CodeGeneratorResponse to stdout. +// +// Plugins written using C++ can use google/protobuf/compiler/plugin.h instead +// of dealing with the raw protocol defined here. +// +// A plugin executable needs only to be placed somewhere in the path. The +// plugin should be named "protoc-gen-$NAME", and will then be used when the +// flag "--${NAME}_out" is passed to protoc. + +syntax = "proto2"; + +package google.protobuf.compiler; +option java_package = "com.google.protobuf.compiler"; +option java_outer_classname = "PluginProtos"; + +option csharp_namespace = "Google.Protobuf.Compiler"; +option go_package = "google.golang.org/protobuf/types/pluginpb"; + +import "google/protobuf/descriptor.proto"; + +// The version number of protocol compiler. +message Version { + optional int32 major = 1; + optional int32 minor = 2; + optional int32 patch = 3; + // A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should + // be empty for mainline stable releases. + optional string suffix = 4; +} + +// An encoded CodeGeneratorRequest is written to the plugin's stdin. +message CodeGeneratorRequest { + // The .proto files that were explicitly listed on the command-line. The + // code generator should generate code only for these files. Each file's + // descriptor will be included in proto_file, below. + repeated string file_to_generate = 1; + + // The generator parameter passed on the command-line. + optional string parameter = 2; + + // FileDescriptorProtos for all files in files_to_generate and everything + // they import. The files will appear in topological order, so each file + // appears before any file that imports it. + // + // protoc guarantees that all proto_files will be written after + // the fields above, even though this is not technically guaranteed by the + // protobuf wire format. This theoretically could allow a plugin to stream + // in the FileDescriptorProtos and handle them one by one rather than read + // the entire set into memory at once. However, as of this writing, this + // is not similarly optimized on protoc's end -- it will store all fields in + // memory at once before sending them to the plugin. + // + // Type names of fields and extensions in the FileDescriptorProto are always + // fully qualified. + repeated FileDescriptorProto proto_file = 15; + + // The version number of protocol compiler. + optional Version compiler_version = 3; +} + +// The plugin writes an encoded CodeGeneratorResponse to stdout. +message CodeGeneratorResponse { + // Error message. If non-empty, code generation failed. The plugin process + // should exit with status code zero even if it reports an error in this way. + // + // This should be used to indicate errors in .proto files which prevent the + // code generator from generating correct code. Errors which indicate a + // problem in protoc itself -- such as the input CodeGeneratorRequest being + // unparseable -- should be reported by writing a message to stderr and + // exiting with a non-zero status code. + optional string error = 1; + + // A bitmask of supported features that the code generator supports. + // This is a bitwise "or" of values from the Feature enum. + optional uint64 supported_features = 2; + + // Sync with code_generator.h. + enum Feature { + FEATURE_NONE = 0; + FEATURE_PROTO3_OPTIONAL = 1; + } + + // Represents a single generated file. + message File { + // The file name, relative to the output directory. The name must not + // contain "." or ".." components and must be relative, not be absolute (so, + // the file cannot lie outside the output directory). "/" must be used as + // the path separator, not "\". + // + // If the name is omitted, the content will be appended to the previous + // file. This allows the generator to break large files into small chunks, + // and allows the generated text to be streamed back to protoc so that large + // files need not reside completely in memory at one time. Note that as of + // this writing protoc does not optimize for this -- it will read the entire + // CodeGeneratorResponse before writing files to disk. + optional string name = 1; + + // If non-empty, indicates that the named file should already exist, and the + // content here is to be inserted into that file at a defined insertion + // point. This feature allows a code generator to extend the output + // produced by another code generator. The original generator may provide + // insertion points by placing special annotations in the file that look + // like: + // @@protoc_insertion_point(NAME) + // The annotation can have arbitrary text before and after it on the line, + // which allows it to be placed in a comment. NAME should be replaced with + // an identifier naming the point -- this is what other generators will use + // as the insertion_point. Code inserted at this point will be placed + // immediately above the line containing the insertion point (thus multiple + // insertions to the same point will come out in the order they were added). + // The double-@ is intended to make it unlikely that the generated code + // could contain things that look like insertion points by accident. + // + // For example, the C++ code generator places the following line in the + // .pb.h files that it generates: + // // @@protoc_insertion_point(namespace_scope) + // This line appears within the scope of the file's package namespace, but + // outside of any particular class. Another plugin can then specify the + // insertion_point "namespace_scope" to generate additional classes or + // other declarations that should be placed in this scope. + // + // Note that if the line containing the insertion point begins with + // whitespace, the same whitespace will be added to every line of the + // inserted text. This is useful for languages like Python, where + // indentation matters. In these languages, the insertion point comment + // should be indented the same amount as any inserted code will need to be + // in order to work correctly in that context. + // + // The code generator that generates the initial file and the one which + // inserts into it must both run as part of a single invocation of protoc. + // Code generators are executed in the order in which they appear on the + // command line. + // + // If |insertion_point| is present, |name| must also be present. + optional string insertion_point = 2; + + // The file contents. + optional string content = 15; + + // Information describing the file content being inserted. If an insertion + // point is used, this information will be appropriately offset and inserted + // into the code generation metadata for the generated files. + optional GeneratedCodeInfo generated_code_info = 16; + } + repeated File file = 15; +} diff --git a/src/examples/app/app_message/protoc-22.2/include/google/protobuf/descriptor.proto b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/descriptor.proto new file mode 100644 index 0000000..3b38675 --- /dev/null +++ b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/descriptor.proto @@ -0,0 +1,975 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// The messages in this file describe the definitions found in .proto files. +// A valid .proto file can be translated directly to a FileDescriptorProto +// without any other information (e.g. without reading its imports). + +syntax = "proto2"; + +package google.protobuf; + +option go_package = "google.golang.org/protobuf/types/descriptorpb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "DescriptorProtos"; +option csharp_namespace = "Google.Protobuf.Reflection"; +option objc_class_prefix = "GPB"; +option cc_enable_arenas = true; + +// descriptor.proto must be optimized for speed because reflection-based +// algorithms don't work during bootstrapping. +option optimize_for = SPEED; + +// The protocol compiler can output a FileDescriptorSet containing the .proto +// files it parses. +message FileDescriptorSet { + repeated FileDescriptorProto file = 1; +} + +// Describes a complete .proto file. +message FileDescriptorProto { + optional string name = 1; // file name, relative to root of source tree + optional string package = 2; // e.g. "foo", "foo.bar", etc. + + // Names of files imported by this file. + repeated string dependency = 3; + // Indexes of the public imported files in the dependency list above. + repeated int32 public_dependency = 10; + // Indexes of the weak imported files in the dependency list. + // For Google-internal migration only. Do not use. + repeated int32 weak_dependency = 11; + + // All top-level definitions in this file. + repeated DescriptorProto message_type = 4; + repeated EnumDescriptorProto enum_type = 5; + repeated ServiceDescriptorProto service = 6; + repeated FieldDescriptorProto extension = 7; + + optional FileOptions options = 8; + + // This field contains optional information about the original source code. + // You may safely remove this entire field without harming runtime + // functionality of the descriptors -- the information is needed only by + // development tools. + optional SourceCodeInfo source_code_info = 9; + + // The syntax of the proto file. + // The supported values are "proto2", "proto3", and "editions". + // + // If `edition` is present, this value must be "editions". + optional string syntax = 12; + + // The edition of the proto file, which is an opaque string. + optional string edition = 13; +} + +// Describes a message type. +message DescriptorProto { + optional string name = 1; + + repeated FieldDescriptorProto field = 2; + repeated FieldDescriptorProto extension = 6; + + repeated DescriptorProto nested_type = 3; + repeated EnumDescriptorProto enum_type = 4; + + message ExtensionRange { + optional int32 start = 1; // Inclusive. + optional int32 end = 2; // Exclusive. + + optional ExtensionRangeOptions options = 3; + } + repeated ExtensionRange extension_range = 5; + + repeated OneofDescriptorProto oneof_decl = 8; + + optional MessageOptions options = 7; + + // Range of reserved tag numbers. Reserved tag numbers may not be used by + // fields or extension ranges in the same message. Reserved ranges may + // not overlap. + message ReservedRange { + optional int32 start = 1; // Inclusive. + optional int32 end = 2; // Exclusive. + } + repeated ReservedRange reserved_range = 9; + // Reserved field names, which may not be used by fields in the same message. + // A given name may only be reserved once. + repeated string reserved_name = 10; +} + +message ExtensionRangeOptions { + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +// Describes a field within a message. +message FieldDescriptorProto { + enum Type { + // 0 is reserved for errors. + // Order is weird for historical reasons. + TYPE_DOUBLE = 1; + TYPE_FLOAT = 2; + // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if + // negative values are likely. + TYPE_INT64 = 3; + TYPE_UINT64 = 4; + // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if + // negative values are likely. + TYPE_INT32 = 5; + TYPE_FIXED64 = 6; + TYPE_FIXED32 = 7; + TYPE_BOOL = 8; + TYPE_STRING = 9; + // Tag-delimited aggregate. + // Group type is deprecated and not supported in proto3. However, Proto3 + // implementations should still be able to parse the group wire format and + // treat group fields as unknown fields. + TYPE_GROUP = 10; + TYPE_MESSAGE = 11; // Length-delimited aggregate. + + // New in version 2. + TYPE_BYTES = 12; + TYPE_UINT32 = 13; + TYPE_ENUM = 14; + TYPE_SFIXED32 = 15; + TYPE_SFIXED64 = 16; + TYPE_SINT32 = 17; // Uses ZigZag encoding. + TYPE_SINT64 = 18; // Uses ZigZag encoding. + } + + enum Label { + // 0 is reserved for errors + LABEL_OPTIONAL = 1; + LABEL_REQUIRED = 2; + LABEL_REPEATED = 3; + } + + optional string name = 1; + optional int32 number = 3; + optional Label label = 4; + + // If type_name is set, this need not be set. If both this and type_name + // are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP. + optional Type type = 5; + + // For message and enum types, this is the name of the type. If the name + // starts with a '.', it is fully-qualified. Otherwise, C++-like scoping + // rules are used to find the type (i.e. first the nested types within this + // message are searched, then within the parent, on up to the root + // namespace). + optional string type_name = 6; + + // For extensions, this is the name of the type being extended. It is + // resolved in the same manner as type_name. + optional string extendee = 2; + + // For numeric types, contains the original text representation of the value. + // For booleans, "true" or "false". + // For strings, contains the default text contents (not escaped in any way). + // For bytes, contains the C escaped value. All bytes >= 128 are escaped. + optional string default_value = 7; + + // If set, gives the index of a oneof in the containing type's oneof_decl + // list. This field is a member of that oneof. + optional int32 oneof_index = 9; + + // JSON name of this field. The value is set by protocol compiler. If the + // user has set a "json_name" option on this field, that option's value + // will be used. Otherwise, it's deduced from the field's name by converting + // it to camelCase. + optional string json_name = 10; + + optional FieldOptions options = 8; + + // If true, this is a proto3 "optional". When a proto3 field is optional, it + // tracks presence regardless of field type. + // + // When proto3_optional is true, this field must be belong to a oneof to + // signal to old proto3 clients that presence is tracked for this field. This + // oneof is known as a "synthetic" oneof, and this field must be its sole + // member (each proto3 optional field gets its own synthetic oneof). Synthetic + // oneofs exist in the descriptor only, and do not generate any API. Synthetic + // oneofs must be ordered after all "real" oneofs. + // + // For message fields, proto3_optional doesn't create any semantic change, + // since non-repeated message fields always track presence. However it still + // indicates the semantic detail of whether the user wrote "optional" or not. + // This can be useful for round-tripping the .proto file. For consistency we + // give message fields a synthetic oneof also, even though it is not required + // to track presence. This is especially important because the parser can't + // tell if a field is a message or an enum, so it must always create a + // synthetic oneof. + // + // Proto2 optional fields do not set this flag, because they already indicate + // optional with `LABEL_OPTIONAL`. + optional bool proto3_optional = 17; +} + +// Describes a oneof. +message OneofDescriptorProto { + optional string name = 1; + optional OneofOptions options = 2; +} + +// Describes an enum type. +message EnumDescriptorProto { + optional string name = 1; + + repeated EnumValueDescriptorProto value = 2; + + optional EnumOptions options = 3; + + // Range of reserved numeric values. Reserved values may not be used by + // entries in the same enum. Reserved ranges may not overlap. + // + // Note that this is distinct from DescriptorProto.ReservedRange in that it + // is inclusive such that it can appropriately represent the entire int32 + // domain. + message EnumReservedRange { + optional int32 start = 1; // Inclusive. + optional int32 end = 2; // Inclusive. + } + + // Range of reserved numeric values. Reserved numeric values may not be used + // by enum values in the same enum declaration. Reserved ranges may not + // overlap. + repeated EnumReservedRange reserved_range = 4; + + // Reserved enum value names, which may not be reused. A given name may only + // be reserved once. + repeated string reserved_name = 5; +} + +// Describes a value within an enum. +message EnumValueDescriptorProto { + optional string name = 1; + optional int32 number = 2; + + optional EnumValueOptions options = 3; +} + +// Describes a service. +message ServiceDescriptorProto { + optional string name = 1; + repeated MethodDescriptorProto method = 2; + + optional ServiceOptions options = 3; +} + +// Describes a method of a service. +message MethodDescriptorProto { + optional string name = 1; + + // Input and output type names. These are resolved in the same way as + // FieldDescriptorProto.type_name, but must refer to a message type. + optional string input_type = 2; + optional string output_type = 3; + + optional MethodOptions options = 4; + + // Identifies if client streams multiple client messages + optional bool client_streaming = 5 [default = false]; + // Identifies if server streams multiple server messages + optional bool server_streaming = 6 [default = false]; +} + +// =================================================================== +// Options + +// Each of the definitions above may have "options" attached. These are +// just annotations which may cause code to be generated slightly differently +// or may contain hints for code that manipulates protocol messages. +// +// Clients may define custom options as extensions of the *Options messages. +// These extensions may not yet be known at parsing time, so the parser cannot +// store the values in them. Instead it stores them in a field in the *Options +// message called uninterpreted_option. This field must have the same name +// across all *Options messages. We then use this field to populate the +// extensions when we build a descriptor, at which point all protos have been +// parsed and so all extensions are known. +// +// Extension numbers for custom options may be chosen as follows: +// * For options which will only be used within a single application or +// organization, or for experimental options, use field numbers 50000 +// through 99999. It is up to you to ensure that you do not use the +// same number for multiple options. +// * For options which will be published and used publicly by multiple +// independent entities, e-mail protobuf-global-extension-registry@google.com +// to reserve extension numbers. Simply provide your project name (e.g. +// Objective-C plugin) and your project website (if available) -- there's no +// need to explain how you intend to use them. Usually you only need one +// extension number. You can declare multiple options with only one extension +// number by putting them in a sub-message. See the Custom Options section of +// the docs for examples: +// https://developers.google.com/protocol-buffers/docs/proto#options +// If this turns out to be popular, a web service will be set up +// to automatically assign option numbers. + +message FileOptions { + + // Sets the Java package where classes generated from this .proto will be + // placed. By default, the proto package is used, but this is often + // inappropriate because proto packages do not normally start with backwards + // domain names. + optional string java_package = 1; + + // Controls the name of the wrapper Java class generated for the .proto file. + // That class will always contain the .proto file's getDescriptor() method as + // well as any top-level extensions defined in the .proto file. + // If java_multiple_files is disabled, then all the other classes from the + // .proto file will be nested inside the single wrapper outer class. + optional string java_outer_classname = 8; + + // If enabled, then the Java code generator will generate a separate .java + // file for each top-level message, enum, and service defined in the .proto + // file. Thus, these types will *not* be nested inside the wrapper class + // named by java_outer_classname. However, the wrapper class will still be + // generated to contain the file's getDescriptor() method as well as any + // top-level extensions defined in the file. + optional bool java_multiple_files = 10 [default = false]; + + // This option does nothing. + optional bool java_generate_equals_and_hash = 20 [deprecated=true]; + + // If set true, then the Java2 code generator will generate code that + // throws an exception whenever an attempt is made to assign a non-UTF-8 + // byte sequence to a string field. + // Message reflection will do the same. + // However, an extension field still accepts non-UTF-8 byte sequences. + // This option has no effect on when used with the lite runtime. + optional bool java_string_check_utf8 = 27 [default = false]; + + // Generated classes can be optimized for speed or code size. + enum OptimizeMode { + SPEED = 1; // Generate complete code for parsing, serialization, + // etc. + CODE_SIZE = 2; // Use ReflectionOps to implement these methods. + LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime. + } + optional OptimizeMode optimize_for = 9 [default = SPEED]; + + // Sets the Go package where structs generated from this .proto will be + // placed. If omitted, the Go package will be derived from the following: + // - The basename of the package import path, if provided. + // - Otherwise, the package statement in the .proto file, if present. + // - Otherwise, the basename of the .proto file, without extension. + optional string go_package = 11; + + // Should generic services be generated in each language? "Generic" services + // are not specific to any particular RPC system. They are generated by the + // main code generators in each language (without additional plugins). + // Generic services were the only kind of service generation supported by + // early versions of google.protobuf. + // + // Generic services are now considered deprecated in favor of using plugins + // that generate code specific to your particular RPC system. Therefore, + // these default to false. Old code which depends on generic services should + // explicitly set them to true. + optional bool cc_generic_services = 16 [default = false]; + optional bool java_generic_services = 17 [default = false]; + optional bool py_generic_services = 18 [default = false]; + optional bool php_generic_services = 42 [default = false]; + + // Is this file deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for everything in the file, or it will be completely ignored; in the very + // least, this is a formalization for deprecating files. + optional bool deprecated = 23 [default = false]; + + // Enables the use of arenas for the proto messages in this file. This applies + // only to generated classes for C++. + optional bool cc_enable_arenas = 31 [default = true]; + + // Sets the objective c class prefix which is prepended to all objective c + // generated classes from this .proto. There is no default. + optional string objc_class_prefix = 36; + + // Namespace for generated classes; defaults to the package. + optional string csharp_namespace = 37; + + // By default Swift generators will take the proto package and CamelCase it + // replacing '.' with underscore and use that to prefix the types/symbols + // defined. When this options is provided, they will use this value instead + // to prefix the types/symbols defined. + optional string swift_prefix = 39; + + // Sets the php class prefix which is prepended to all php generated classes + // from this .proto. Default is empty. + optional string php_class_prefix = 40; + + // Use this option to change the namespace of php generated classes. Default + // is empty. When this option is empty, the package name will be used for + // determining the namespace. + optional string php_namespace = 41; + + // Use this option to change the namespace of php generated metadata classes. + // Default is empty. When this option is empty, the proto file name will be + // used for determining the namespace. + optional string php_metadata_namespace = 44; + + // Use this option to change the package of ruby generated classes. Default + // is empty. When this option is not set, the package name will be used for + // determining the ruby package. + optional string ruby_package = 45; + + // The parser stores options it doesn't recognize here. + // See the documentation for the "Options" section above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. + // See the documentation for the "Options" section above. + extensions 1000 to max; + + reserved 38; +} + +message MessageOptions { + // Set true to use the old proto1 MessageSet wire format for extensions. + // This is provided for backwards-compatibility with the MessageSet wire + // format. You should not use this for any other reason: It's less + // efficient, has fewer features, and is more complicated. + // + // The message must be defined exactly as follows: + // message Foo { + // option message_set_wire_format = true; + // extensions 4 to max; + // } + // Note that the message cannot have any defined fields; MessageSets only + // have extensions. + // + // All extensions of your type must be singular messages; e.g. they cannot + // be int32s, enums, or repeated messages. + // + // Because this is an option, the above two restrictions are not enforced by + // the protocol compiler. + optional bool message_set_wire_format = 1 [default = false]; + + // Disables the generation of the standard "descriptor()" accessor, which can + // conflict with a field of the same name. This is meant to make migration + // from proto1 easier; new code should avoid fields named "descriptor". + optional bool no_standard_descriptor_accessor = 2 [default = false]; + + // Is this message deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the message, or it will be completely ignored; in the very least, + // this is a formalization for deprecating messages. + optional bool deprecated = 3 [default = false]; + + reserved 4, 5, 6; + + // NOTE: Do not set the option in .proto files. Always use the maps syntax + // instead. The option should only be implicitly set by the proto compiler + // parser. + // + // Whether the message is an automatically generated map entry type for the + // maps field. + // + // For maps fields: + // map map_field = 1; + // The parsed descriptor looks like: + // message MapFieldEntry { + // option map_entry = true; + // optional KeyType key = 1; + // optional ValueType value = 2; + // } + // repeated MapFieldEntry map_field = 1; + // + // Implementations may choose not to generate the map_entry=true message, but + // use a native map in the target language to hold the keys and values. + // The reflection APIs in such implementations still need to work as + // if the field is a repeated message field. + optional bool map_entry = 7; + + reserved 8; // javalite_serializable + reserved 9; // javanano_as_lite + + // Enable the legacy handling of JSON field name conflicts. This lowercases + // and strips underscored from the fields before comparison in proto3 only. + // The new behavior takes `json_name` into account and applies to proto2 as + // well. + // + // This should only be used as a temporary measure against broken builds due + // to the change in behavior for JSON field name conflicts. + // + // TODO(b/261750190) This is legacy behavior we plan to remove once downstream + // teams have had time to migrate. + optional bool deprecated_legacy_json_field_conflicts = 11 [deprecated = true]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message FieldOptions { + // The ctype option instructs the C++ code generator to use a different + // representation of the field than it normally would. See the specific + // options below. This option is not yet implemented in the open source + // release -- sorry, we'll try to include it in a future version! + optional CType ctype = 1 [default = STRING]; + enum CType { + // Default mode. + STRING = 0; + + CORD = 1; + + STRING_PIECE = 2; + } + // The packed option can be enabled for repeated primitive fields to enable + // a more efficient representation on the wire. Rather than repeatedly + // writing the tag and type for each element, the entire array is encoded as + // a single length-delimited blob. In proto3, only explicit setting it to + // false will avoid using packed encoding. + optional bool packed = 2; + + // The jstype option determines the JavaScript type used for values of the + // field. The option is permitted only for 64 bit integral and fixed types + // (int64, uint64, sint64, fixed64, sfixed64). A field with jstype JS_STRING + // is represented as JavaScript string, which avoids loss of precision that + // can happen when a large value is converted to a floating point JavaScript. + // Specifying JS_NUMBER for the jstype causes the generated JavaScript code to + // use the JavaScript "number" type. The behavior of the default option + // JS_NORMAL is implementation dependent. + // + // This option is an enum to permit additional types to be added, e.g. + // goog.math.Integer. + optional JSType jstype = 6 [default = JS_NORMAL]; + enum JSType { + // Use the default type. + JS_NORMAL = 0; + + // Use JavaScript strings. + JS_STRING = 1; + + // Use JavaScript numbers. + JS_NUMBER = 2; + } + + // Should this field be parsed lazily? Lazy applies only to message-type + // fields. It means that when the outer message is initially parsed, the + // inner message's contents will not be parsed but instead stored in encoded + // form. The inner message will actually be parsed when it is first accessed. + // + // This is only a hint. Implementations are free to choose whether to use + // eager or lazy parsing regardless of the value of this option. However, + // setting this option true suggests that the protocol author believes that + // using lazy parsing on this field is worth the additional bookkeeping + // overhead typically needed to implement it. + // + // This option does not affect the public interface of any generated code; + // all method signatures remain the same. Furthermore, thread-safety of the + // interface is not affected by this option; const methods remain safe to + // call from multiple threads concurrently, while non-const methods continue + // to require exclusive access. + // + // Note that implementations may choose not to check required fields within + // a lazy sub-message. That is, calling IsInitialized() on the outer message + // may return true even if the inner message has missing required fields. + // This is necessary because otherwise the inner message would have to be + // parsed in order to perform the check, defeating the purpose of lazy + // parsing. An implementation which chooses not to check required fields + // must be consistent about it. That is, for any particular sub-message, the + // implementation must either *always* check its required fields, or *never* + // check its required fields, regardless of whether or not the message has + // been parsed. + // + // As of May 2022, lazy verifies the contents of the byte stream during + // parsing. An invalid byte stream will cause the overall parsing to fail. + optional bool lazy = 5 [default = false]; + + // unverified_lazy does no correctness checks on the byte stream. This should + // only be used where lazy with verification is prohibitive for performance + // reasons. + optional bool unverified_lazy = 15 [default = false]; + + // Is this field deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for accessors, or it will be completely ignored; in the very least, this + // is a formalization for deprecating fields. + optional bool deprecated = 3 [default = false]; + + // For Google-internal migration only. Do not use. + optional bool weak = 10 [default = false]; + + // Indicate that the field value should not be printed out when using debug + // formats, e.g. when the field contains sensitive credentials. + optional bool debug_redact = 16 [default = false]; + + // If set to RETENTION_SOURCE, the option will be omitted from the binary. + // Note: as of January 2023, support for this is in progress and does not yet + // have an effect (b/264593489). + enum OptionRetention { + RETENTION_UNKNOWN = 0; + RETENTION_RUNTIME = 1; + RETENTION_SOURCE = 2; + } + + optional OptionRetention retention = 17; + + // This indicates the types of entities that the field may apply to when used + // as an option. If it is unset, then the field may be freely used as an + // option on any kind of entity. Note: as of January 2023, support for this is + // in progress and does not yet have an effect (b/264593489). + enum OptionTargetType { + TARGET_TYPE_UNKNOWN = 0; + TARGET_TYPE_FILE = 1; + TARGET_TYPE_EXTENSION_RANGE = 2; + TARGET_TYPE_MESSAGE = 3; + TARGET_TYPE_FIELD = 4; + TARGET_TYPE_ONEOF = 5; + TARGET_TYPE_ENUM = 6; + TARGET_TYPE_ENUM_ENTRY = 7; + TARGET_TYPE_SERVICE = 8; + TARGET_TYPE_METHOD = 9; + } + + optional OptionTargetType target = 18; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; + + reserved 4; // removed jtype +} + +message OneofOptions { + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message EnumOptions { + + // Set this option to true to allow mapping different tag names to the same + // value. + optional bool allow_alias = 2; + + // Is this enum deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the enum, or it will be completely ignored; in the very least, this + // is a formalization for deprecating enums. + optional bool deprecated = 3 [default = false]; + + reserved 5; // javanano_as_lite + + // Enable the legacy handling of JSON field name conflicts. This lowercases + // and strips underscored from the fields before comparison in proto3 only. + // The new behavior takes `json_name` into account and applies to proto2 as + // well. + // TODO(b/261750190) Remove this legacy behavior once downstream teams have + // had time to migrate. + optional bool deprecated_legacy_json_field_conflicts = 6 [deprecated = true]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message EnumValueOptions { + // Is this enum value deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the enum value, or it will be completely ignored; in the very least, + // this is a formalization for deprecating enum values. + optional bool deprecated = 1 [default = false]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message ServiceOptions { + + // Note: Field numbers 1 through 32 are reserved for Google's internal RPC + // framework. We apologize for hoarding these numbers to ourselves, but + // we were already using them long before we decided to release Protocol + // Buffers. + + // Is this service deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the service, or it will be completely ignored; in the very least, + // this is a formalization for deprecating services. + optional bool deprecated = 33 [default = false]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message MethodOptions { + + // Note: Field numbers 1 through 32 are reserved for Google's internal RPC + // framework. We apologize for hoarding these numbers to ourselves, but + // we were already using them long before we decided to release Protocol + // Buffers. + + // Is this method deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the method, or it will be completely ignored; in the very least, + // this is a formalization for deprecating methods. + optional bool deprecated = 33 [default = false]; + + // Is this method side-effect-free (or safe in HTTP parlance), or idempotent, + // or neither? HTTP based RPC implementation may choose GET verb for safe + // methods, and PUT verb for idempotent methods instead of the default POST. + enum IdempotencyLevel { + IDEMPOTENCY_UNKNOWN = 0; + NO_SIDE_EFFECTS = 1; // implies idempotent + IDEMPOTENT = 2; // idempotent, but may have side effects + } + optional IdempotencyLevel idempotency_level = 34 + [default = IDEMPOTENCY_UNKNOWN]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +// A message representing a option the parser does not recognize. This only +// appears in options protos created by the compiler::Parser class. +// DescriptorPool resolves these when building Descriptor objects. Therefore, +// options protos in descriptor objects (e.g. returned by Descriptor::options(), +// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions +// in them. +message UninterpretedOption { + // The name of the uninterpreted option. Each string represents a segment in + // a dot-separated name. is_extension is true iff a segment represents an + // extension (denoted with parentheses in options specs in .proto files). + // E.g.,{ ["foo", false], ["bar.baz", true], ["moo", false] } represents + // "foo.(bar.baz).moo". + message NamePart { + required string name_part = 1; + required bool is_extension = 2; + } + repeated NamePart name = 2; + + // The value of the uninterpreted option, in whatever type the tokenizer + // identified it as during parsing. Exactly one of these should be set. + optional string identifier_value = 3; + optional uint64 positive_int_value = 4; + optional int64 negative_int_value = 5; + optional double double_value = 6; + optional bytes string_value = 7; + optional string aggregate_value = 8; +} + +// =================================================================== +// Optional source code info + +// Encapsulates information about the original source file from which a +// FileDescriptorProto was generated. +message SourceCodeInfo { + // A Location identifies a piece of source code in a .proto file which + // corresponds to a particular definition. This information is intended + // to be useful to IDEs, code indexers, documentation generators, and similar + // tools. + // + // For example, say we have a file like: + // message Foo { + // optional string foo = 1; + // } + // Let's look at just the field definition: + // optional string foo = 1; + // ^ ^^ ^^ ^ ^^^ + // a bc de f ghi + // We have the following locations: + // span path represents + // [a,i) [ 4, 0, 2, 0 ] The whole field definition. + // [a,b) [ 4, 0, 2, 0, 4 ] The label (optional). + // [c,d) [ 4, 0, 2, 0, 5 ] The type (string). + // [e,f) [ 4, 0, 2, 0, 1 ] The name (foo). + // [g,h) [ 4, 0, 2, 0, 3 ] The number (1). + // + // Notes: + // - A location may refer to a repeated field itself (i.e. not to any + // particular index within it). This is used whenever a set of elements are + // logically enclosed in a single code segment. For example, an entire + // extend block (possibly containing multiple extension definitions) will + // have an outer location whose path refers to the "extensions" repeated + // field without an index. + // - Multiple locations may have the same path. This happens when a single + // logical declaration is spread out across multiple places. The most + // obvious example is the "extend" block again -- there may be multiple + // extend blocks in the same scope, each of which will have the same path. + // - A location's span is not always a subset of its parent's span. For + // example, the "extendee" of an extension declaration appears at the + // beginning of the "extend" block and is shared by all extensions within + // the block. + // - Just because a location's span is a subset of some other location's span + // does not mean that it is a descendant. For example, a "group" defines + // both a type and a field in a single declaration. Thus, the locations + // corresponding to the type and field and their components will overlap. + // - Code which tries to interpret locations should probably be designed to + // ignore those that it doesn't understand, as more types of locations could + // be recorded in the future. + repeated Location location = 1; + message Location { + // Identifies which part of the FileDescriptorProto was defined at this + // location. + // + // Each element is a field number or an index. They form a path from + // the root FileDescriptorProto to the place where the definition occurs. + // For example, this path: + // [ 4, 3, 2, 7, 1 ] + // refers to: + // file.message_type(3) // 4, 3 + // .field(7) // 2, 7 + // .name() // 1 + // This is because FileDescriptorProto.message_type has field number 4: + // repeated DescriptorProto message_type = 4; + // and DescriptorProto.field has field number 2: + // repeated FieldDescriptorProto field = 2; + // and FieldDescriptorProto.name has field number 1: + // optional string name = 1; + // + // Thus, the above path gives the location of a field name. If we removed + // the last element: + // [ 4, 3, 2, 7 ] + // this path refers to the whole field declaration (from the beginning + // of the label to the terminating semicolon). + repeated int32 path = 1 [packed = true]; + + // Always has exactly three or four elements: start line, start column, + // end line (optional, otherwise assumed same as start line), end column. + // These are packed into a single field for efficiency. Note that line + // and column numbers are zero-based -- typically you will want to add + // 1 to each before displaying to a user. + repeated int32 span = 2 [packed = true]; + + // If this SourceCodeInfo represents a complete declaration, these are any + // comments appearing before and after the declaration which appear to be + // attached to the declaration. + // + // A series of line comments appearing on consecutive lines, with no other + // tokens appearing on those lines, will be treated as a single comment. + // + // leading_detached_comments will keep paragraphs of comments that appear + // before (but not connected to) the current element. Each paragraph, + // separated by empty lines, will be one comment element in the repeated + // field. + // + // Only the comment content is provided; comment markers (e.g. //) are + // stripped out. For block comments, leading whitespace and an asterisk + // will be stripped from the beginning of each line other than the first. + // Newlines are included in the output. + // + // Examples: + // + // optional int32 foo = 1; // Comment attached to foo. + // // Comment attached to bar. + // optional int32 bar = 2; + // + // optional string baz = 3; + // // Comment attached to baz. + // // Another line attached to baz. + // + // // Comment attached to moo. + // // + // // Another line attached to moo. + // optional double moo = 4; + // + // // Detached comment for corge. This is not leading or trailing comments + // // to moo or corge because there are blank lines separating it from + // // both. + // + // // Detached comment for corge paragraph 2. + // + // optional string corge = 5; + // /* Block comment attached + // * to corge. Leading asterisks + // * will be removed. */ + // /* Block comment attached to + // * grault. */ + // optional int32 grault = 6; + // + // // ignored detached comments. + optional string leading_comments = 3; + optional string trailing_comments = 4; + repeated string leading_detached_comments = 6; + } +} + +// Describes the relationship between generated code and its original source +// file. A GeneratedCodeInfo message is associated with only one generated +// source file, but may contain references to different source .proto files. +message GeneratedCodeInfo { + // An Annotation connects some span of text in generated code to an element + // of its generating .proto file. + repeated Annotation annotation = 1; + message Annotation { + // Identifies the element in the original source .proto file. This field + // is formatted the same as SourceCodeInfo.Location.path. + repeated int32 path = 1 [packed = true]; + + // Identifies the filesystem path to the original source .proto. + optional string source_file = 2; + + // Identifies the starting offset in bytes in the generated code + // that relates to the identified object. + optional int32 begin = 3; + + // Identifies the ending offset in bytes in the generated code that + // relates to the identified object. The end offset should be one past + // the last relevant byte (so the length of the text = end - begin). + optional int32 end = 4; + + // Represents the identified object's effect on the element in the original + // .proto file. + enum Semantic { + // There is no effect or the effect is indescribable. + NONE = 0; + // The element is set or otherwise mutated. + SET = 1; + // An alias to the element is returned. + ALIAS = 2; + } + optional Semantic semantic = 5; + } +} diff --git a/src/examples/app/app_message/protoc-22.2/include/google/protobuf/duration.proto b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/duration.proto new file mode 100644 index 0000000..41f40c2 --- /dev/null +++ b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/duration.proto @@ -0,0 +1,115 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/protobuf/types/known/durationpb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "DurationProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; + +// A Duration represents a signed, fixed-length span of time represented +// as a count of seconds and fractions of seconds at nanosecond +// resolution. It is independent of any calendar and concepts like "day" +// or "month". It is related to Timestamp in that the difference between +// two Timestamp values is a Duration and it can be added or subtracted +// from a Timestamp. Range is approximately +-10,000 years. +// +// # Examples +// +// Example 1: Compute Duration from two Timestamps in pseudo code. +// +// Timestamp start = ...; +// Timestamp end = ...; +// Duration duration = ...; +// +// duration.seconds = end.seconds - start.seconds; +// duration.nanos = end.nanos - start.nanos; +// +// if (duration.seconds < 0 && duration.nanos > 0) { +// duration.seconds += 1; +// duration.nanos -= 1000000000; +// } else if (duration.seconds > 0 && duration.nanos < 0) { +// duration.seconds -= 1; +// duration.nanos += 1000000000; +// } +// +// Example 2: Compute Timestamp from Timestamp + Duration in pseudo code. +// +// Timestamp start = ...; +// Duration duration = ...; +// Timestamp end = ...; +// +// end.seconds = start.seconds + duration.seconds; +// end.nanos = start.nanos + duration.nanos; +// +// if (end.nanos < 0) { +// end.seconds -= 1; +// end.nanos += 1000000000; +// } else if (end.nanos >= 1000000000) { +// end.seconds += 1; +// end.nanos -= 1000000000; +// } +// +// Example 3: Compute Duration from datetime.timedelta in Python. +// +// td = datetime.timedelta(days=3, minutes=10) +// duration = Duration() +// duration.FromTimedelta(td) +// +// # JSON Mapping +// +// In JSON format, the Duration type is encoded as a string rather than an +// object, where the string ends in the suffix "s" (indicating seconds) and +// is preceded by the number of seconds, with nanoseconds expressed as +// fractional seconds. For example, 3 seconds with 0 nanoseconds should be +// encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should +// be expressed in JSON format as "3.000000001s", and 3 seconds and 1 +// microsecond should be expressed in JSON format as "3.000001s". +// +message Duration { + // Signed seconds of the span of time. Must be from -315,576,000,000 + // to +315,576,000,000 inclusive. Note: these bounds are computed from: + // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years + int64 seconds = 1; + + // Signed fractions of a second at nanosecond resolution of the span + // of time. Durations less than one second are represented with a 0 + // `seconds` field and a positive or negative `nanos` field. For durations + // of one second or more, a non-zero value for the `nanos` field must be + // of the same sign as the `seconds` field. Must be from -999,999,999 + // to +999,999,999 inclusive. + int32 nanos = 2; +} diff --git a/src/examples/app/app_message/protoc-22.2/include/google/protobuf/empty.proto b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/empty.proto new file mode 100644 index 0000000..b87c89d --- /dev/null +++ b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/empty.proto @@ -0,0 +1,51 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option go_package = "google.golang.org/protobuf/types/known/emptypb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "EmptyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; + +// A generic empty message that you can re-use to avoid defining duplicated +// empty messages in your APIs. A typical example is to use it as the request +// or the response type of an API method. For instance: +// +// service Foo { +// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); +// } +// +message Empty {} diff --git a/src/examples/app/app_message/protoc-22.2/include/google/protobuf/field_mask.proto b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/field_mask.proto new file mode 100644 index 0000000..b28334b --- /dev/null +++ b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/field_mask.proto @@ -0,0 +1,245 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option java_package = "com.google.protobuf"; +option java_outer_classname = "FieldMaskProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "google.golang.org/protobuf/types/known/fieldmaskpb"; +option cc_enable_arenas = true; + +// `FieldMask` represents a set of symbolic field paths, for example: +// +// paths: "f.a" +// paths: "f.b.d" +// +// Here `f` represents a field in some root message, `a` and `b` +// fields in the message found in `f`, and `d` a field found in the +// message in `f.b`. +// +// Field masks are used to specify a subset of fields that should be +// returned by a get operation or modified by an update operation. +// Field masks also have a custom JSON encoding (see below). +// +// # Field Masks in Projections +// +// When used in the context of a projection, a response message or +// sub-message is filtered by the API to only contain those fields as +// specified in the mask. For example, if the mask in the previous +// example is applied to a response message as follows: +// +// f { +// a : 22 +// b { +// d : 1 +// x : 2 +// } +// y : 13 +// } +// z: 8 +// +// The result will not contain specific values for fields x,y and z +// (their value will be set to the default, and omitted in proto text +// output): +// +// +// f { +// a : 22 +// b { +// d : 1 +// } +// } +// +// A repeated field is not allowed except at the last position of a +// paths string. +// +// If a FieldMask object is not present in a get operation, the +// operation applies to all fields (as if a FieldMask of all fields +// had been specified). +// +// Note that a field mask does not necessarily apply to the +// top-level response message. In case of a REST get operation, the +// field mask applies directly to the response, but in case of a REST +// list operation, the mask instead applies to each individual message +// in the returned resource list. In case of a REST custom method, +// other definitions may be used. Where the mask applies will be +// clearly documented together with its declaration in the API. In +// any case, the effect on the returned resource/resources is required +// behavior for APIs. +// +// # Field Masks in Update Operations +// +// A field mask in update operations specifies which fields of the +// targeted resource are going to be updated. The API is required +// to only change the values of the fields as specified in the mask +// and leave the others untouched. If a resource is passed in to +// describe the updated values, the API ignores the values of all +// fields not covered by the mask. +// +// If a repeated field is specified for an update operation, new values will +// be appended to the existing repeated field in the target resource. Note that +// a repeated field is only allowed in the last position of a `paths` string. +// +// If a sub-message is specified in the last position of the field mask for an +// update operation, then new value will be merged into the existing sub-message +// in the target resource. +// +// For example, given the target message: +// +// f { +// b { +// d: 1 +// x: 2 +// } +// c: [1] +// } +// +// And an update message: +// +// f { +// b { +// d: 10 +// } +// c: [2] +// } +// +// then if the field mask is: +// +// paths: ["f.b", "f.c"] +// +// then the result will be: +// +// f { +// b { +// d: 10 +// x: 2 +// } +// c: [1, 2] +// } +// +// An implementation may provide options to override this default behavior for +// repeated and message fields. +// +// In order to reset a field's value to the default, the field must +// be in the mask and set to the default value in the provided resource. +// Hence, in order to reset all fields of a resource, provide a default +// instance of the resource and set all fields in the mask, or do +// not provide a mask as described below. +// +// If a field mask is not present on update, the operation applies to +// all fields (as if a field mask of all fields has been specified). +// Note that in the presence of schema evolution, this may mean that +// fields the client does not know and has therefore not filled into +// the request will be reset to their default. If this is unwanted +// behavior, a specific service may require a client to always specify +// a field mask, producing an error if not. +// +// As with get operations, the location of the resource which +// describes the updated values in the request message depends on the +// operation kind. In any case, the effect of the field mask is +// required to be honored by the API. +// +// ## Considerations for HTTP REST +// +// The HTTP kind of an update operation which uses a field mask must +// be set to PATCH instead of PUT in order to satisfy HTTP semantics +// (PUT must only be used for full updates). +// +// # JSON Encoding of Field Masks +// +// In JSON, a field mask is encoded as a single string where paths are +// separated by a comma. Fields name in each path are converted +// to/from lower-camel naming conventions. +// +// As an example, consider the following message declarations: +// +// message Profile { +// User user = 1; +// Photo photo = 2; +// } +// message User { +// string display_name = 1; +// string address = 2; +// } +// +// In proto a field mask for `Profile` may look as such: +// +// mask { +// paths: "user.display_name" +// paths: "photo" +// } +// +// In JSON, the same mask is represented as below: +// +// { +// mask: "user.displayName,photo" +// } +// +// # Field Masks and Oneof Fields +// +// Field masks treat fields in oneofs just as regular fields. Consider the +// following message: +// +// message SampleMessage { +// oneof test_oneof { +// string name = 4; +// SubMessage sub_message = 9; +// } +// } +// +// The field mask can be: +// +// mask { +// paths: "name" +// } +// +// Or: +// +// mask { +// paths: "sub_message" +// } +// +// Note that oneof type names ("test_oneof" in this case) cannot be used in +// paths. +// +// ## Field Mask Verification +// +// The implementation of any API method which has a FieldMask type field in the +// request should verify the included field paths, and return an +// `INVALID_ARGUMENT` error if any path is unmappable. +message FieldMask { + // The set of field mask paths. + repeated string paths = 1; +} diff --git a/src/examples/app/app_message/protoc-22.2/include/google/protobuf/source_context.proto b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/source_context.proto new file mode 100644 index 0000000..135f50f --- /dev/null +++ b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/source_context.proto @@ -0,0 +1,48 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option java_package = "com.google.protobuf"; +option java_outer_classname = "SourceContextProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "google.golang.org/protobuf/types/known/sourcecontextpb"; + +// `SourceContext` represents information about the source of a +// protobuf element, like the file in which it is defined. +message SourceContext { + // The path-qualified name of the .proto file that contained the associated + // protobuf element. For example: `"google/protobuf/source_context.proto"`. + string file_name = 1; +} diff --git a/src/examples/app/app_message/protoc-22.2/include/google/protobuf/struct.proto b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/struct.proto new file mode 100644 index 0000000..c4ea645 --- /dev/null +++ b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/struct.proto @@ -0,0 +1,95 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/protobuf/types/known/structpb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "StructProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; + +// `Struct` represents a structured data value, consisting of fields +// which map to dynamically typed values. In some languages, `Struct` +// might be supported by a native representation. For example, in +// scripting languages like JS a struct is represented as an +// object. The details of that representation are described together +// with the proto support for the language. +// +// The JSON representation for `Struct` is JSON object. +message Struct { + // Unordered map of dynamically typed values. + map fields = 1; +} + +// `Value` represents a dynamically typed value which can be either +// null, a number, a string, a boolean, a recursive struct value, or a +// list of values. A producer of value is expected to set one of these +// variants. Absence of any variant indicates an error. +// +// The JSON representation for `Value` is JSON value. +message Value { + // The kind of value. + oneof kind { + // Represents a null value. + NullValue null_value = 1; + // Represents a double value. + double number_value = 2; + // Represents a string value. + string string_value = 3; + // Represents a boolean value. + bool bool_value = 4; + // Represents a structured value. + Struct struct_value = 5; + // Represents a repeated `Value`. + ListValue list_value = 6; + } +} + +// `NullValue` is a singleton enumeration to represent the null value for the +// `Value` type union. +// +// The JSON representation for `NullValue` is JSON `null`. +enum NullValue { + // Null value. + NULL_VALUE = 0; +} + +// `ListValue` is a wrapper around a repeated field of values. +// +// The JSON representation for `ListValue` is JSON array. +message ListValue { + // Repeated field of dynamically typed values. + repeated Value values = 1; +} diff --git a/src/examples/app/app_message/protoc-22.2/include/google/protobuf/timestamp.proto b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/timestamp.proto new file mode 100644 index 0000000..2fb527c --- /dev/null +++ b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/timestamp.proto @@ -0,0 +1,144 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/protobuf/types/known/timestamppb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "TimestampProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; + +// A Timestamp represents a point in time independent of any time zone or local +// calendar, encoded as a count of seconds and fractions of seconds at +// nanosecond resolution. The count is relative to an epoch at UTC midnight on +// January 1, 1970, in the proleptic Gregorian calendar which extends the +// Gregorian calendar backwards to year one. +// +// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap +// second table is needed for interpretation, using a [24-hour linear +// smear](https://developers.google.com/time/smear). +// +// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By +// restricting to that range, we ensure that we can convert to and from [RFC +// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. +// +// # Examples +// +// Example 1: Compute Timestamp from POSIX `time()`. +// +// Timestamp timestamp; +// timestamp.set_seconds(time(NULL)); +// timestamp.set_nanos(0); +// +// Example 2: Compute Timestamp from POSIX `gettimeofday()`. +// +// struct timeval tv; +// gettimeofday(&tv, NULL); +// +// Timestamp timestamp; +// timestamp.set_seconds(tv.tv_sec); +// timestamp.set_nanos(tv.tv_usec * 1000); +// +// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. +// +// FILETIME ft; +// GetSystemTimeAsFileTime(&ft); +// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; +// +// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z +// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. +// Timestamp timestamp; +// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); +// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); +// +// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. +// +// long millis = System.currentTimeMillis(); +// +// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) +// .setNanos((int) ((millis % 1000) * 1000000)).build(); +// +// Example 5: Compute Timestamp from Java `Instant.now()`. +// +// Instant now = Instant.now(); +// +// Timestamp timestamp = +// Timestamp.newBuilder().setSeconds(now.getEpochSecond()) +// .setNanos(now.getNano()).build(); +// +// Example 6: Compute Timestamp from current time in Python. +// +// timestamp = Timestamp() +// timestamp.GetCurrentTime() +// +// # JSON Mapping +// +// In JSON format, the Timestamp type is encoded as a string in the +// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the +// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" +// where {year} is always expressed using four digits while {month}, {day}, +// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional +// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), +// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone +// is required. A proto3 JSON serializer should always use UTC (as indicated by +// "Z") when printing the Timestamp type and a proto3 JSON parser should be +// able to accept both UTC and other timezones (as indicated by an offset). +// +// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past +// 01:30 UTC on January 15, 2017. +// +// In JavaScript, one can convert a Date object to this format using the +// standard +// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) +// method. In Python, a standard `datetime.datetime` object can be converted +// to this format using +// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with +// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use +// the Joda Time's [`ISODateTimeFormat.dateTime()`]( +// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D +// ) to obtain a formatter capable of generating timestamps in this format. +// +message Timestamp { + // Represents seconds of UTC time since Unix epoch + // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + // 9999-12-31T23:59:59Z inclusive. + int64 seconds = 1; + + // Non-negative fractions of a second at nanosecond resolution. Negative + // second values with fractions must still have non-negative nanos values + // that count forward in time. Must be from 0 to 999,999,999 + // inclusive. + int32 nanos = 2; +} diff --git a/src/examples/app/app_message/protoc-22.2/include/google/protobuf/type.proto b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/type.proto new file mode 100644 index 0000000..fd25a41 --- /dev/null +++ b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/type.proto @@ -0,0 +1,187 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +import "google/protobuf/any.proto"; +import "google/protobuf/source_context.proto"; + +option cc_enable_arenas = true; +option java_package = "com.google.protobuf"; +option java_outer_classname = "TypeProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "google.golang.org/protobuf/types/known/typepb"; + +// A protocol buffer message type. +message Type { + // The fully qualified message name. + string name = 1; + // The list of fields. + repeated Field fields = 2; + // The list of types appearing in `oneof` definitions in this type. + repeated string oneofs = 3; + // The protocol buffer options. + repeated Option options = 4; + // The source context. + SourceContext source_context = 5; + // The source syntax. + Syntax syntax = 6; +} + +// A single field of a message type. +message Field { + // Basic field types. + enum Kind { + // Field type unknown. + TYPE_UNKNOWN = 0; + // Field type double. + TYPE_DOUBLE = 1; + // Field type float. + TYPE_FLOAT = 2; + // Field type int64. + TYPE_INT64 = 3; + // Field type uint64. + TYPE_UINT64 = 4; + // Field type int32. + TYPE_INT32 = 5; + // Field type fixed64. + TYPE_FIXED64 = 6; + // Field type fixed32. + TYPE_FIXED32 = 7; + // Field type bool. + TYPE_BOOL = 8; + // Field type string. + TYPE_STRING = 9; + // Field type group. Proto2 syntax only, and deprecated. + TYPE_GROUP = 10; + // Field type message. + TYPE_MESSAGE = 11; + // Field type bytes. + TYPE_BYTES = 12; + // Field type uint32. + TYPE_UINT32 = 13; + // Field type enum. + TYPE_ENUM = 14; + // Field type sfixed32. + TYPE_SFIXED32 = 15; + // Field type sfixed64. + TYPE_SFIXED64 = 16; + // Field type sint32. + TYPE_SINT32 = 17; + // Field type sint64. + TYPE_SINT64 = 18; + } + + // Whether a field is optional, required, or repeated. + enum Cardinality { + // For fields with unknown cardinality. + CARDINALITY_UNKNOWN = 0; + // For optional fields. + CARDINALITY_OPTIONAL = 1; + // For required fields. Proto2 syntax only. + CARDINALITY_REQUIRED = 2; + // For repeated fields. + CARDINALITY_REPEATED = 3; + } + + // The field type. + Kind kind = 1; + // The field cardinality. + Cardinality cardinality = 2; + // The field number. + int32 number = 3; + // The field name. + string name = 4; + // The field type URL, without the scheme, for message or enumeration + // types. Example: `"type.googleapis.com/google.protobuf.Timestamp"`. + string type_url = 6; + // The index of the field type in `Type.oneofs`, for message or enumeration + // types. The first type has index 1; zero means the type is not in the list. + int32 oneof_index = 7; + // Whether to use alternative packed wire representation. + bool packed = 8; + // The protocol buffer options. + repeated Option options = 9; + // The field JSON name. + string json_name = 10; + // The string value of the default value of this field. Proto2 syntax only. + string default_value = 11; +} + +// Enum type definition. +message Enum { + // Enum type name. + string name = 1; + // Enum value definitions. + repeated EnumValue enumvalue = 2; + // Protocol buffer options. + repeated Option options = 3; + // The source context. + SourceContext source_context = 4; + // The source syntax. + Syntax syntax = 5; +} + +// Enum value definition. +message EnumValue { + // Enum value name. + string name = 1; + // Enum value number. + int32 number = 2; + // Protocol buffer options. + repeated Option options = 3; +} + +// A protocol buffer option, which can be attached to a message, field, +// enumeration, etc. +message Option { + // The option's name. For protobuf built-in options (options defined in + // descriptor.proto), this is the short name. For example, `"map_entry"`. + // For custom options, it should be the fully-qualified name. For example, + // `"google.api.http"`. + string name = 1; + // The option's value packed in an Any message. If the value is a primitive, + // the corresponding wrapper type defined in google/protobuf/wrappers.proto + // should be used. If the value is an enum, it should be stored as an int32 + // value using the google.protobuf.Int32Value type. + Any value = 2; +} + +// The syntax in which a protocol buffer element is defined. +enum Syntax { + // Syntax `proto2`. + SYNTAX_PROTO2 = 0; + // Syntax `proto3`. + SYNTAX_PROTO3 = 1; +} diff --git a/src/examples/app/app_message/protoc-22.2/include/google/protobuf/wrappers.proto b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/wrappers.proto new file mode 100644 index 0000000..1959fa5 --- /dev/null +++ b/src/examples/app/app_message/protoc-22.2/include/google/protobuf/wrappers.proto @@ -0,0 +1,123 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Wrappers for primitive (non-message) types. These types are useful +// for embedding primitives in the `google.protobuf.Any` type and for places +// where we need to distinguish between the absence of a primitive +// typed field and its default value. +// +// These wrappers have no meaningful use within repeated fields as they lack +// the ability to detect presence on individual elements. +// These wrappers have no meaningful use within a map or a oneof since +// individual entries of a map or fields of a oneof can already detect presence. + +syntax = "proto3"; + +package google.protobuf; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/protobuf/types/known/wrapperspb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "WrappersProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; + +// Wrapper message for `double`. +// +// The JSON representation for `DoubleValue` is JSON number. +message DoubleValue { + // The double value. + double value = 1; +} + +// Wrapper message for `float`. +// +// The JSON representation for `FloatValue` is JSON number. +message FloatValue { + // The float value. + float value = 1; +} + +// Wrapper message for `int64`. +// +// The JSON representation for `Int64Value` is JSON string. +message Int64Value { + // The int64 value. + int64 value = 1; +} + +// Wrapper message for `uint64`. +// +// The JSON representation for `UInt64Value` is JSON string. +message UInt64Value { + // The uint64 value. + uint64 value = 1; +} + +// Wrapper message for `int32`. +// +// The JSON representation for `Int32Value` is JSON number. +message Int32Value { + // The int32 value. + int32 value = 1; +} + +// Wrapper message for `uint32`. +// +// The JSON representation for `UInt32Value` is JSON number. +message UInt32Value { + // The uint32 value. + uint32 value = 1; +} + +// Wrapper message for `bool`. +// +// The JSON representation for `BoolValue` is JSON `true` and `false`. +message BoolValue { + // The bool value. + bool value = 1; +} + +// Wrapper message for `string`. +// +// The JSON representation for `StringValue` is JSON string. +message StringValue { + // The string value. + string value = 1; +} + +// Wrapper message for `bytes`. +// +// The JSON representation for `BytesValue` is JSON string. +message BytesValue { + // The bytes value. + bytes value = 1; +} diff --git a/src/examples/app/app_message/protoc-22.2/readme.txt b/src/examples/app/app_message/protoc-22.2/readme.txt new file mode 100644 index 0000000..f7f091f --- /dev/null +++ b/src/examples/app/app_message/protoc-22.2/readme.txt @@ -0,0 +1,12 @@ +Protocol Buffers - Google's data interchange format +Copyright 2008 Google Inc. +https://developers.google.com/protocol-buffers/ +This package contains a precompiled binary version of the protocol buffer +compiler (protoc). This binary is intended for users who want to use Protocol +Buffers in languages other than C++ but do not want to compile protoc +themselves. To install, simply place this binary somewhere in your PATH. +If you intend to use the included well known types then don't forget to +copy the contents of the 'include' directory somewhere as well, for example +into '/usr/local/include/'. +Please refer to our official github site for more installation instructions: + https://github.com/protocolbuffers/protobuf diff --git a/src/examples/app/app_message/protos/draw_data_storage.proto b/src/examples/app/app_message/protos/draw_data_storage.proto new file mode 100644 index 0000000..5cf6407 --- /dev/null +++ b/src/examples/app/app_message/protos/draw_data_storage.proto @@ -0,0 +1,63 @@ +syntax = "proto3"; + +package graphicData; + +message RtssGraphicStorage { + Canvas canvas = 1; + repeated Link links = 2; +} + +message Canvas { + // 画布宽 + int32 width = 1; + // 画布高 + int32 height = 2; + // 背景色 + string backgroundColor = 3; + // 视口变换 + Transform viewportTransform = 4; +} + +message Point { + // x坐标 + float x = 1; + // y坐标 + float y = 2; +} + +//变换 +message Transform { + // 位移 + Point position = 1; + // 缩放 + Point scale = 2; + // 旋转弧度 + float rotation = 3; + // 歪斜 + Point skew = 4; +} + +//子元素变换 +message ChildTransform { + // 子元素名称 + string name = 1; + // 子元素变换 + Transform transform = 2; +} +// 公共属性 +message CommonInfo { + string id = 1; + string graphicType = 2; + Transform transform = 3; + repeated ChildTransform childTransforms = 4; +} + +message Link { + CommonInfo common = 1; + string code = 2; + bool curve = 3; // 是否曲线 + int32 segmentsCount = 4; // 曲线分段数 + int32 lineWidth = 5; // 线宽 + string lineColor = 6; // 线色 + repeated Point points = 7; // 点坐标列表 +} diff --git a/src/examples/app/graphics/LinkInteraction.ts b/src/examples/app/graphics/LinkInteraction.ts new file mode 100644 index 0000000..6f23466 --- /dev/null +++ b/src/examples/app/graphics/LinkInteraction.ts @@ -0,0 +1,109 @@ +import * as pb_1 from 'google-protobuf'; +import { IPointData } from 'pixi.js'; +import { ILinkData } from 'src/graphics/link/Link'; +import { ChildTransform, GraphicTransform } from 'src/jlgraphic'; +import { toStorageTransform } from '..'; +import { graphicData } from '../protos/draw_data_storage'; + +export class LinkData implements ILinkData { + data: graphicData.Link; + constructor(data?: graphicData.Link) { + if (data) { + this.data = data; + } else { + this.data = new graphicData.Link({ + common: new graphicData.CommonInfo(), + }); + } + } + get code(): string { + return this.data.code; + } + set code(v: string) { + this.data.code = v; + } + get curve(): boolean { + return this.data.curve; + } + set curve(v: boolean) { + this.data.curve = v; + } + get segmentsCount(): number { + return this.data.segmentsCount; + } + set segmentsCount(v: number) { + this.data.segmentsCount = v; + } + get points(): IPointData[] { + return this.data.points; + } + set points(points: IPointData[]) { + this.data.points = points.map( + (p) => new graphicData.Point({ x: p.x, y: p.y }) + ); + } + get lineWidth(): number { + return this.data.lineWidth; + } + set lineWidth(v: number) { + this.data.lineWidth = v; + } + get lineColor(): string { + return this.data.lineColor; + } + set lineColor(v: string) { + this.data.lineColor = v; + } + clone(): LinkData { + return new LinkData(this.data.cloneMessage()); + } + copyFrom(data: LinkData): void { + pb_1.Message.copyInto(data.data, this.data); + } + eq(other: LinkData): boolean { + return pb_1.Message.equals(this.data, other.data); + } + get id(): string { + return this.data.common.id; + } + set id(v: string) { + this.data.common.id = v; + } + get graphicType(): string { + return this.data.common.graphicType; + } + set graphicType(v: string) { + this.data.common.graphicType = v; + } + get transform(): GraphicTransform { + return GraphicTransform.from(this.data.common.transform); + } + set transform(v: GraphicTransform) { + this.data.common.transform = toStorageTransform(v); + } + get childTransforms(): ChildTransform[] | undefined { + const cts: ChildTransform[] = []; + if (this.data.common.childTransforms) { + this.data.common.childTransforms.forEach((ct) => { + cts.push(ChildTransform.from(ct)); + }); + } + return cts; + } + set childTransforms(v: ChildTransform[] | undefined) { + if (v) { + const cts: graphicData.ChildTransform[] = []; + v.forEach((ct) => + cts.push( + new graphicData.ChildTransform({ + ...ct, + transform: toStorageTransform(ct.transform), + }) + ) + ); + this.data.common.childTransforms = cts; + } else { + this.data.common.childTransforms = []; + } + } +} diff --git a/src/examples/app/index.ts b/src/examples/app/index.ts new file mode 100644 index 0000000..9c00df7 --- /dev/null +++ b/src/examples/app/index.ts @@ -0,0 +1,111 @@ +import { fromUint8Array, toUint8Array } from 'js-base64'; +import { IPointData, Point } from 'pixi.js'; +import { Link } from 'src/graphics/link/Link'; +import { LinkDraw } from 'src/graphics/link/LinkDrawAssistant'; +import { + CombinationKey, + GraphicApp, + GraphicData, + GraphicTransform, + JlDrawApp, + KeyListener, +} from 'src/jlgraphic'; +import { LinkData } from './graphics/LinkInteraction'; +import { graphicData } from './protos/draw_data_storage'; + +export function fromStoragePoint(p: graphicData.Point): Point { + return new Point(p.x, p.y); +} + +export function toStoragePoint(p: IPointData): graphicData.Point { + return new graphicData.Point({ x: p.x, y: p.y }); +} + +export function fromStorageTransfrom( + transfrom: graphicData.Transform +): GraphicTransform { + return new GraphicTransform( + fromStoragePoint(transfrom.position), + fromStoragePoint(transfrom.scale), + transfrom.rotation, + fromStoragePoint(transfrom.skew) + ); +} + +export function toStorageTransform( + transform: GraphicTransform +): graphicData.Transform { + return new graphicData.Transform({ + position: toStoragePoint(transform.position), + scale: toStoragePoint(transform.scale), + rotation: transform.rotation, + skew: toStoragePoint(transform.skew), + }); +} + +export function initDrawApp(app: JlDrawApp) { + app.setOptions({ + drawAssistants: [ + new LinkDraw(app, { + newData: () => { + return new LinkData(); + }, + }), + ], + }); + app.addKeyboardListener( + new KeyListener({ + value: 'KeyL', + onPress: () => { + app.interactionPlugin(Link.Type).resume(); + }, + }) + ); + app.addKeyboardListener( + new KeyListener({ + value: 'KeyS', + global: true, + combinations: [CombinationKey.Ctrl], + onPress: () => { + saveDrawDatas(app); + }, + }) + ); +} + +const StorageKey = 'graphic-storage'; +export function saveDrawDatas(app: JlDrawApp) { + const storage = new graphicData.RtssGraphicStorage(); + const canvasData = app.canvas.saveData(); + storage.canvas = new graphicData.Canvas({ + ...canvasData, + viewportTransform: toStorageTransform(canvasData.viewportTransform), + }); + const graphics = app.queryStore.getAllGraphics(); + graphics.forEach((g) => { + if (Link.Type === g.type) { + const linkData = (g as Link).saveData(); + storage.links.push((linkData as LinkData).data); + } + }); + const base64 = fromUint8Array(storage.serialize()); + console.log('保存数据', storage); + localStorage.setItem(StorageKey, base64); +} + +export function loadDrawDatas(app: GraphicApp) { + // localStorage.removeItem(StorageKey); + const base64 = localStorage.getItem(StorageKey); + if (base64) { + const storage = graphicData.RtssGraphicStorage.deserialize( + toUint8Array(base64) + ); + console.log('加载数据', storage); + app.updateCanvas(storage.canvas); + const datas: GraphicData[] = []; + storage.links.forEach((link) => { + datas.push(new LinkData(link)); + }); + app.loadGraphic(datas); + } +} diff --git a/src/examples/app/protos/draw_data_storage.ts b/src/examples/app/protos/draw_data_storage.ts new file mode 100644 index 0000000..bb9fc4b --- /dev/null +++ b/src/examples/app/protos/draw_data_storage.ts @@ -0,0 +1,1079 @@ +/** + * Generated by the protoc-gen-ts. DO NOT EDIT! + * compiler version: 4.22.2 + * source: draw_data_storage.proto + * git: https://github.com/thesayyn/protoc-gen-ts */ +import * as pb_1 from 'google-protobuf'; +export namespace graphicData { + export class RtssGraphicStorage extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor( + data?: + | any[] + | { + canvas?: Canvas; + links?: Link[]; + } + ) { + super(); + pb_1.Message.initialize( + this, + Array.isArray(data) ? data : [], + 0, + -1, + [2], + this.#one_of_decls + ); + if (!Array.isArray(data) && typeof data == 'object') { + if ('canvas' in data && data.canvas != undefined) { + this.canvas = data.canvas; + } + if ('links' in data && data.links != undefined) { + this.links = data.links; + } + } + } + get canvas() { + return pb_1.Message.getWrapperField(this, Canvas, 1) as Canvas; + } + set canvas(value: Canvas) { + pb_1.Message.setWrapperField(this, 1, value); + } + get has_canvas() { + return pb_1.Message.getField(this, 1) != null; + } + get links() { + return pb_1.Message.getRepeatedWrapperField(this, Link, 2) as Link[]; + } + set links(value: Link[]) { + pb_1.Message.setRepeatedWrapperField(this, 2, value); + } + static fromObject(data: { + canvas?: ReturnType; + links?: ReturnType[]; + }): RtssGraphicStorage { + const message = new RtssGraphicStorage({}); + if (data.canvas != null) { + message.canvas = Canvas.fromObject(data.canvas); + } + if (data.links != null) { + message.links = data.links.map((item) => Link.fromObject(item)); + } + return message; + } + toObject() { + const data: { + canvas?: ReturnType; + links?: ReturnType[]; + } = {}; + if (this.canvas != null) { + data.canvas = this.canvas.toObject(); + } + if (this.links != null) { + data.links = this.links.map((item: Link) => item.toObject()); + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.has_canvas) + writer.writeMessage(1, this.canvas, () => + this.canvas.serialize(writer) + ); + if (this.links.length) + writer.writeRepeatedMessage(2, this.links, (item: Link) => + item.serialize(writer) + ); + if (!w) return writer.getResultBuffer(); + } + static deserialize( + bytes: Uint8Array | pb_1.BinaryReader + ): RtssGraphicStorage { + const reader = + bytes instanceof pb_1.BinaryReader + ? bytes + : new pb_1.BinaryReader(bytes), + message = new RtssGraphicStorage(); + while (reader.nextField()) { + if (reader.isEndGroup()) break; + switch (reader.getFieldNumber()) { + case 1: + reader.readMessage( + message.canvas, + () => (message.canvas = Canvas.deserialize(reader)) + ); + break; + case 2: + reader.readMessage(message.links, () => + pb_1.Message.addToRepeatedWrapperField( + message, + 2, + Link.deserialize(reader), + Link + ) + ); + break; + default: + reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): RtssGraphicStorage { + return RtssGraphicStorage.deserialize(bytes); + } + } + export class Canvas extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor( + data?: + | any[] + | { + width?: number; + height?: number; + backgroundColor?: string; + viewportTransform?: Transform; + } + ) { + super(); + pb_1.Message.initialize( + this, + Array.isArray(data) ? data : [], + 0, + -1, + [], + this.#one_of_decls + ); + if (!Array.isArray(data) && typeof data == 'object') { + if ('width' in data && data.width != undefined) { + this.width = data.width; + } + if ('height' in data && data.height != undefined) { + this.height = data.height; + } + if ('backgroundColor' in data && data.backgroundColor != undefined) { + this.backgroundColor = data.backgroundColor; + } + if ( + 'viewportTransform' in data && + data.viewportTransform != undefined + ) { + this.viewportTransform = data.viewportTransform; + } + } + } + get width() { + return pb_1.Message.getFieldWithDefault(this, 1, 0) as number; + } + set width(value: number) { + pb_1.Message.setField(this, 1, value); + } + get height() { + return pb_1.Message.getFieldWithDefault(this, 2, 0) as number; + } + set height(value: number) { + pb_1.Message.setField(this, 2, value); + } + get backgroundColor() { + return pb_1.Message.getFieldWithDefault(this, 3, '') as string; + } + set backgroundColor(value: string) { + pb_1.Message.setField(this, 3, value); + } + get viewportTransform() { + return pb_1.Message.getWrapperField(this, Transform, 4) as Transform; + } + set viewportTransform(value: Transform) { + pb_1.Message.setWrapperField(this, 4, value); + } + get has_viewportTransform() { + return pb_1.Message.getField(this, 4) != null; + } + static fromObject(data: { + width?: number; + height?: number; + backgroundColor?: string; + viewportTransform?: ReturnType; + }): Canvas { + const message = new Canvas({}); + if (data.width != null) { + message.width = data.width; + } + if (data.height != null) { + message.height = data.height; + } + if (data.backgroundColor != null) { + message.backgroundColor = data.backgroundColor; + } + if (data.viewportTransform != null) { + message.viewportTransform = Transform.fromObject( + data.viewportTransform + ); + } + return message; + } + toObject() { + const data: { + width?: number; + height?: number; + backgroundColor?: string; + viewportTransform?: ReturnType; + } = {}; + if (this.width != null) { + data.width = this.width; + } + if (this.height != null) { + data.height = this.height; + } + if (this.backgroundColor != null) { + data.backgroundColor = this.backgroundColor; + } + if (this.viewportTransform != null) { + data.viewportTransform = this.viewportTransform.toObject(); + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.width != 0) writer.writeInt32(1, this.width); + if (this.height != 0) writer.writeInt32(2, this.height); + if (this.backgroundColor.length) + writer.writeString(3, this.backgroundColor); + if (this.has_viewportTransform) + writer.writeMessage(4, this.viewportTransform, () => + this.viewportTransform.serialize(writer) + ); + if (!w) return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): Canvas { + const reader = + bytes instanceof pb_1.BinaryReader + ? bytes + : new pb_1.BinaryReader(bytes), + message = new Canvas(); + while (reader.nextField()) { + if (reader.isEndGroup()) break; + switch (reader.getFieldNumber()) { + case 1: + message.width = reader.readInt32(); + break; + case 2: + message.height = reader.readInt32(); + break; + case 3: + message.backgroundColor = reader.readString(); + break; + case 4: + reader.readMessage( + message.viewportTransform, + () => (message.viewportTransform = Transform.deserialize(reader)) + ); + break; + default: + reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): Canvas { + return Canvas.deserialize(bytes); + } + } + export class Point extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor( + data?: + | any[] + | { + x?: number; + y?: number; + } + ) { + super(); + pb_1.Message.initialize( + this, + Array.isArray(data) ? data : [], + 0, + -1, + [], + this.#one_of_decls + ); + if (!Array.isArray(data) && typeof data == 'object') { + if ('x' in data && data.x != undefined) { + this.x = data.x; + } + if ('y' in data && data.y != undefined) { + this.y = data.y; + } + } + } + get x() { + return pb_1.Message.getFieldWithDefault(this, 1, 0) as number; + } + set x(value: number) { + pb_1.Message.setField(this, 1, value); + } + get y() { + return pb_1.Message.getFieldWithDefault(this, 2, 0) as number; + } + set y(value: number) { + pb_1.Message.setField(this, 2, value); + } + static fromObject(data: { x?: number; y?: number }): Point { + const message = new Point({}); + if (data.x != null) { + message.x = data.x; + } + if (data.y != null) { + message.y = data.y; + } + return message; + } + toObject() { + const data: { + x?: number; + y?: number; + } = {}; + if (this.x != null) { + data.x = this.x; + } + if (this.y != null) { + data.y = this.y; + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.x != 0) writer.writeFloat(1, this.x); + if (this.y != 0) writer.writeFloat(2, this.y); + if (!w) return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): Point { + const reader = + bytes instanceof pb_1.BinaryReader + ? bytes + : new pb_1.BinaryReader(bytes), + message = new Point(); + while (reader.nextField()) { + if (reader.isEndGroup()) break; + switch (reader.getFieldNumber()) { + case 1: + message.x = reader.readFloat(); + break; + case 2: + message.y = reader.readFloat(); + break; + default: + reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): Point { + return Point.deserialize(bytes); + } + } + export class Transform extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor( + data?: + | any[] + | { + position?: Point; + scale?: Point; + rotation?: number; + skew?: Point; + } + ) { + super(); + pb_1.Message.initialize( + this, + Array.isArray(data) ? data : [], + 0, + -1, + [], + this.#one_of_decls + ); + if (!Array.isArray(data) && typeof data == 'object') { + if ('position' in data && data.position != undefined) { + this.position = data.position; + } + if ('scale' in data && data.scale != undefined) { + this.scale = data.scale; + } + if ('rotation' in data && data.rotation != undefined) { + this.rotation = data.rotation; + } + if ('skew' in data && data.skew != undefined) { + this.skew = data.skew; + } + } + } + get position() { + return pb_1.Message.getWrapperField(this, Point, 1) as Point; + } + set position(value: Point) { + pb_1.Message.setWrapperField(this, 1, value); + } + get has_position() { + return pb_1.Message.getField(this, 1) != null; + } + get scale() { + return pb_1.Message.getWrapperField(this, Point, 2) as Point; + } + set scale(value: Point) { + pb_1.Message.setWrapperField(this, 2, value); + } + get has_scale() { + return pb_1.Message.getField(this, 2) != null; + } + get rotation() { + return pb_1.Message.getFieldWithDefault(this, 3, 0) as number; + } + set rotation(value: number) { + pb_1.Message.setField(this, 3, value); + } + get skew() { + return pb_1.Message.getWrapperField(this, Point, 4) as Point; + } + set skew(value: Point) { + pb_1.Message.setWrapperField(this, 4, value); + } + get has_skew() { + return pb_1.Message.getField(this, 4) != null; + } + static fromObject(data: { + position?: ReturnType; + scale?: ReturnType; + rotation?: number; + skew?: ReturnType; + }): Transform { + const message = new Transform({}); + if (data.position != null) { + message.position = Point.fromObject(data.position); + } + if (data.scale != null) { + message.scale = Point.fromObject(data.scale); + } + if (data.rotation != null) { + message.rotation = data.rotation; + } + if (data.skew != null) { + message.skew = Point.fromObject(data.skew); + } + return message; + } + toObject() { + const data: { + position?: ReturnType; + scale?: ReturnType; + rotation?: number; + skew?: ReturnType; + } = {}; + if (this.position != null) { + data.position = this.position.toObject(); + } + if (this.scale != null) { + data.scale = this.scale.toObject(); + } + if (this.rotation != null) { + data.rotation = this.rotation; + } + if (this.skew != null) { + data.skew = this.skew.toObject(); + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.has_position) + writer.writeMessage(1, this.position, () => + this.position.serialize(writer) + ); + if (this.has_scale) + writer.writeMessage(2, this.scale, () => this.scale.serialize(writer)); + if (this.rotation != 0) writer.writeFloat(3, this.rotation); + if (this.has_skew) + writer.writeMessage(4, this.skew, () => this.skew.serialize(writer)); + if (!w) return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): Transform { + const reader = + bytes instanceof pb_1.BinaryReader + ? bytes + : new pb_1.BinaryReader(bytes), + message = new Transform(); + while (reader.nextField()) { + if (reader.isEndGroup()) break; + switch (reader.getFieldNumber()) { + case 1: + reader.readMessage( + message.position, + () => (message.position = Point.deserialize(reader)) + ); + break; + case 2: + reader.readMessage( + message.scale, + () => (message.scale = Point.deserialize(reader)) + ); + break; + case 3: + message.rotation = reader.readFloat(); + break; + case 4: + reader.readMessage( + message.skew, + () => (message.skew = Point.deserialize(reader)) + ); + break; + default: + reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): Transform { + return Transform.deserialize(bytes); + } + } + export class ChildTransform extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor( + data?: + | any[] + | { + name?: string; + transform?: Transform; + } + ) { + super(); + pb_1.Message.initialize( + this, + Array.isArray(data) ? data : [], + 0, + -1, + [], + this.#one_of_decls + ); + if (!Array.isArray(data) && typeof data == 'object') { + if ('name' in data && data.name != undefined) { + this.name = data.name; + } + if ('transform' in data && data.transform != undefined) { + this.transform = data.transform; + } + } + } + get name() { + return pb_1.Message.getFieldWithDefault(this, 1, '') as string; + } + set name(value: string) { + pb_1.Message.setField(this, 1, value); + } + get transform() { + return pb_1.Message.getWrapperField(this, Transform, 2) as Transform; + } + set transform(value: Transform) { + pb_1.Message.setWrapperField(this, 2, value); + } + get has_transform() { + return pb_1.Message.getField(this, 2) != null; + } + static fromObject(data: { + name?: string; + transform?: ReturnType; + }): ChildTransform { + const message = new ChildTransform({}); + if (data.name != null) { + message.name = data.name; + } + if (data.transform != null) { + message.transform = Transform.fromObject(data.transform); + } + return message; + } + toObject() { + const data: { + name?: string; + transform?: ReturnType; + } = {}; + if (this.name != null) { + data.name = this.name; + } + if (this.transform != null) { + data.transform = this.transform.toObject(); + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.name.length) writer.writeString(1, this.name); + if (this.has_transform) + writer.writeMessage(2, this.transform, () => + this.transform.serialize(writer) + ); + if (!w) return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): ChildTransform { + const reader = + bytes instanceof pb_1.BinaryReader + ? bytes + : new pb_1.BinaryReader(bytes), + message = new ChildTransform(); + while (reader.nextField()) { + if (reader.isEndGroup()) break; + switch (reader.getFieldNumber()) { + case 1: + message.name = reader.readString(); + break; + case 2: + reader.readMessage( + message.transform, + () => (message.transform = Transform.deserialize(reader)) + ); + break; + default: + reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): ChildTransform { + return ChildTransform.deserialize(bytes); + } + } + export class CommonInfo extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor( + data?: + | any[] + | { + id?: string; + graphicType?: string; + transform?: Transform; + childTransforms?: ChildTransform[]; + } + ) { + super(); + pb_1.Message.initialize( + this, + Array.isArray(data) ? data : [], + 0, + -1, + [4], + this.#one_of_decls + ); + if (!Array.isArray(data) && typeof data == 'object') { + if ('id' in data && data.id != undefined) { + this.id = data.id; + } + if ('graphicType' in data && data.graphicType != undefined) { + this.graphicType = data.graphicType; + } + if ('transform' in data && data.transform != undefined) { + this.transform = data.transform; + } + if ('childTransforms' in data && data.childTransforms != undefined) { + this.childTransforms = data.childTransforms; + } + } + } + get id() { + return pb_1.Message.getFieldWithDefault(this, 1, '') as string; + } + set id(value: string) { + pb_1.Message.setField(this, 1, value); + } + get graphicType() { + return pb_1.Message.getFieldWithDefault(this, 2, '') as string; + } + set graphicType(value: string) { + pb_1.Message.setField(this, 2, value); + } + get transform() { + return pb_1.Message.getWrapperField(this, Transform, 3) as Transform; + } + set transform(value: Transform) { + pb_1.Message.setWrapperField(this, 3, value); + } + get has_transform() { + return pb_1.Message.getField(this, 3) != null; + } + get childTransforms() { + return pb_1.Message.getRepeatedWrapperField( + this, + ChildTransform, + 4 + ) as ChildTransform[]; + } + set childTransforms(value: ChildTransform[]) { + pb_1.Message.setRepeatedWrapperField(this, 4, value); + } + static fromObject(data: { + id?: string; + graphicType?: string; + transform?: ReturnType; + childTransforms?: ReturnType[]; + }): CommonInfo { + const message = new CommonInfo({}); + if (data.id != null) { + message.id = data.id; + } + if (data.graphicType != null) { + message.graphicType = data.graphicType; + } + if (data.transform != null) { + message.transform = Transform.fromObject(data.transform); + } + if (data.childTransforms != null) { + message.childTransforms = data.childTransforms.map((item) => + ChildTransform.fromObject(item) + ); + } + return message; + } + toObject() { + const data: { + id?: string; + graphicType?: string; + transform?: ReturnType; + childTransforms?: ReturnType< + typeof ChildTransform.prototype.toObject + >[]; + } = {}; + if (this.id != null) { + data.id = this.id; + } + if (this.graphicType != null) { + data.graphicType = this.graphicType; + } + if (this.transform != null) { + data.transform = this.transform.toObject(); + } + if (this.childTransforms != null) { + data.childTransforms = this.childTransforms.map( + (item: ChildTransform) => item.toObject() + ); + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.id.length) writer.writeString(1, this.id); + if (this.graphicType.length) writer.writeString(2, this.graphicType); + if (this.has_transform) + writer.writeMessage(3, this.transform, () => + this.transform.serialize(writer) + ); + if (this.childTransforms.length) + writer.writeRepeatedMessage( + 4, + this.childTransforms, + (item: ChildTransform) => item.serialize(writer) + ); + if (!w) return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): CommonInfo { + const reader = + bytes instanceof pb_1.BinaryReader + ? bytes + : new pb_1.BinaryReader(bytes), + message = new CommonInfo(); + while (reader.nextField()) { + if (reader.isEndGroup()) break; + switch (reader.getFieldNumber()) { + case 1: + message.id = reader.readString(); + break; + case 2: + message.graphicType = reader.readString(); + break; + case 3: + reader.readMessage( + message.transform, + () => (message.transform = Transform.deserialize(reader)) + ); + break; + case 4: + reader.readMessage(message.childTransforms, () => + pb_1.Message.addToRepeatedWrapperField( + message, + 4, + ChildTransform.deserialize(reader), + ChildTransform + ) + ); + break; + default: + reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): CommonInfo { + return CommonInfo.deserialize(bytes); + } + } + export class Link extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor( + data?: + | any[] + | { + common?: CommonInfo; + code?: string; + curve?: boolean; + segmentsCount?: number; + lineWidth?: number; + lineColor?: string; + points?: Point[]; + } + ) { + super(); + pb_1.Message.initialize( + this, + Array.isArray(data) ? data : [], + 0, + -1, + [7], + this.#one_of_decls + ); + if (!Array.isArray(data) && typeof data == 'object') { + if ('common' in data && data.common != undefined) { + this.common = data.common; + } + if ('code' in data && data.code != undefined) { + this.code = data.code; + } + if ('curve' in data && data.curve != undefined) { + this.curve = data.curve; + } + if ('segmentsCount' in data && data.segmentsCount != undefined) { + this.segmentsCount = data.segmentsCount; + } + if ('lineWidth' in data && data.lineWidth != undefined) { + this.lineWidth = data.lineWidth; + } + if ('lineColor' in data && data.lineColor != undefined) { + this.lineColor = data.lineColor; + } + if ('points' in data && data.points != undefined) { + this.points = data.points; + } + } + } + get common() { + return pb_1.Message.getWrapperField(this, CommonInfo, 1) as CommonInfo; + } + set common(value: CommonInfo) { + pb_1.Message.setWrapperField(this, 1, value); + } + get has_common() { + return pb_1.Message.getField(this, 1) != null; + } + get code() { + return pb_1.Message.getFieldWithDefault(this, 2, '') as string; + } + set code(value: string) { + pb_1.Message.setField(this, 2, value); + } + get curve() { + return pb_1.Message.getFieldWithDefault(this, 3, false) as boolean; + } + set curve(value: boolean) { + pb_1.Message.setField(this, 3, value); + } + get segmentsCount() { + return pb_1.Message.getFieldWithDefault(this, 4, 0) as number; + } + set segmentsCount(value: number) { + pb_1.Message.setField(this, 4, value); + } + get lineWidth() { + return pb_1.Message.getFieldWithDefault(this, 5, 0) as number; + } + set lineWidth(value: number) { + pb_1.Message.setField(this, 5, value); + } + get lineColor() { + return pb_1.Message.getFieldWithDefault(this, 6, '') as string; + } + set lineColor(value: string) { + pb_1.Message.setField(this, 6, value); + } + get points() { + return pb_1.Message.getRepeatedWrapperField(this, Point, 7) as Point[]; + } + set points(value: Point[]) { + pb_1.Message.setRepeatedWrapperField(this, 7, value); + } + static fromObject(data: { + common?: ReturnType; + code?: string; + curve?: boolean; + segmentsCount?: number; + lineWidth?: number; + lineColor?: string; + points?: ReturnType[]; + }): Link { + const message = new Link({}); + if (data.common != null) { + message.common = CommonInfo.fromObject(data.common); + } + if (data.code != null) { + message.code = data.code; + } + if (data.curve != null) { + message.curve = data.curve; + } + if (data.segmentsCount != null) { + message.segmentsCount = data.segmentsCount; + } + if (data.lineWidth != null) { + message.lineWidth = data.lineWidth; + } + if (data.lineColor != null) { + message.lineColor = data.lineColor; + } + if (data.points != null) { + message.points = data.points.map((item) => Point.fromObject(item)); + } + return message; + } + toObject() { + const data: { + common?: ReturnType; + code?: string; + curve?: boolean; + segmentsCount?: number; + lineWidth?: number; + lineColor?: string; + points?: ReturnType[]; + } = {}; + if (this.common != null) { + data.common = this.common.toObject(); + } + if (this.code != null) { + data.code = this.code; + } + if (this.curve != null) { + data.curve = this.curve; + } + if (this.segmentsCount != null) { + data.segmentsCount = this.segmentsCount; + } + if (this.lineWidth != null) { + data.lineWidth = this.lineWidth; + } + if (this.lineColor != null) { + data.lineColor = this.lineColor; + } + if (this.points != null) { + data.points = this.points.map((item: Point) => item.toObject()); + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.has_common) + writer.writeMessage(1, this.common, () => + this.common.serialize(writer) + ); + if (this.code.length) writer.writeString(2, this.code); + if (this.curve != false) writer.writeBool(3, this.curve); + if (this.segmentsCount != 0) writer.writeInt32(4, this.segmentsCount); + if (this.lineWidth != 0) writer.writeInt32(5, this.lineWidth); + if (this.lineColor.length) writer.writeString(6, this.lineColor); + if (this.points.length) + writer.writeRepeatedMessage(7, this.points, (item: Point) => + item.serialize(writer) + ); + if (!w) return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): Link { + const reader = + bytes instanceof pb_1.BinaryReader + ? bytes + : new pb_1.BinaryReader(bytes), + message = new Link(); + while (reader.nextField()) { + if (reader.isEndGroup()) break; + switch (reader.getFieldNumber()) { + case 1: + reader.readMessage( + message.common, + () => (message.common = CommonInfo.deserialize(reader)) + ); + break; + case 2: + message.code = reader.readString(); + break; + case 3: + message.curve = reader.readBool(); + break; + case 4: + message.segmentsCount = reader.readInt32(); + break; + case 5: + message.lineWidth = reader.readInt32(); + break; + case 6: + message.lineColor = reader.readString(); + break; + case 7: + reader.readMessage(message.points, () => + pb_1.Message.addToRepeatedWrapperField( + message, + 7, + Point.deserialize(reader), + Point + ) + ); + break; + default: + reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): Link { + return Link.deserialize(bytes); + } + } +} diff --git a/src/graphics/link/Link.ts b/src/graphics/link/Link.ts new file mode 100644 index 0000000..4ea66fd --- /dev/null +++ b/src/graphics/link/Link.ts @@ -0,0 +1,93 @@ +import { Color, Graphics, IPointData } from 'pixi.js'; +import { + GraphicData, + JlGraphic, + JlGraphicTemplate, + convertToBezierPoints, +} from 'src/jlgraphic'; + +export interface ILinkData extends GraphicData { + get code(): string; // 编号 + set code(v: string); + get curve(): boolean; // 是否曲线 + set curve(v: boolean); + get segmentsCount(): number; // 曲线分段数 + set segmentsCount(v: number); + get points(): IPointData[]; // 线坐标点 + set points(points: IPointData[]); + get lineWidth(): number; // 线宽 + set lineWidth(v: number); + get lineColor(): string; // 线色 + set lineColor(v: string); + clone(): ILinkData; + copyFrom(data: ILinkData): void; + eq(other: ILinkData): boolean; +} + +export class Link extends JlGraphic { + static Type = 'Link'; + lineGraphic: Graphics; + constructor() { + super(Link.Type); + this.lineGraphic = new Graphics(); + this.addChild(this.lineGraphic); + } + get datas(): ILinkData { + return this.getDatas(); + } + doRepaint(): void { + if (this.datas.points.length < 2) { + throw new Error('Link坐标数据异常'); + } + this.lineGraphic.clear(); + this.lineGraphic.lineStyle( + this.datas.lineWidth, + new Color(this.datas.lineColor) + ); + if (this.datas.curve) { + // 曲线 + const bps = convertToBezierPoints(this.datas.points); + bps.forEach((bp) => { + this.lineGraphic.drawBezierCurve( + bp.p1, + bp.p2, + bp.cp1, + bp.cp2, + this.datas.segmentsCount + ); + }); + } else { + // 直线 + const start = this.getStartPoint(); + this.lineGraphic.moveTo(start.x, start.y); + for (let i = 0; i < this.datas.points.length; i++) { + const p = this.datas.points[i]; + this.lineGraphic.lineTo(p.x, p.y); + } + } + } + + getStartPoint(): IPointData { + return this.datas.points[0]; + } + getEndPoint(): IPointData { + return this.datas.points[this.datas.points.length - 1]; + } +} + +export class LinkTemplate extends JlGraphicTemplate { + curve: boolean; + lineWidth: number; + lineColor: string; + segmentsCount: number; + constructor() { + super(Link.Type); + this.lineWidth = 2; + this.lineColor = '#000000'; + this.curve = false; + this.segmentsCount = 10; + } + new(): Link { + return new Link(); + } +} diff --git a/src/graphics/link/LinkDrawAssistant.ts b/src/graphics/link/LinkDrawAssistant.ts new file mode 100644 index 0000000..d7c8fca --- /dev/null +++ b/src/graphics/link/LinkDrawAssistant.ts @@ -0,0 +1,343 @@ +import { + Color, + DisplayObject, + FederatedMouseEvent, + FederatedPointerEvent, + Graphics, + IHitArea, + Point, +} from 'pixi.js'; +import { + AbsorbablePosition, + DraggablePoint, + GraphicApp, + GraphicDrawAssistant, + GraphicInteractionPlugin, + JlDrawApp, + JlGraphic, + KeyListener, + calculateMirrorPoint, + convertToBezierPoints, + linePoint, + pointPolygon, +} from 'src/jlgraphic'; +import AbsorbablePoint, { + AbsorbableCircle, +} from 'src/jlgraphic/graphic/AbsorbablePosition'; +import { + BezierCurveEditPlugin, + PolylineEditPlugin, +} from 'src/jlgraphic/plugins/GraphicEditPlugin'; +import { ILinkData, Link, LinkTemplate } from './Link'; + +export interface ILinkDrawOptions { + newData: () => ILinkData; +} + +export class LinkDraw extends GraphicDrawAssistant { + _options: ILinkDrawOptions; + points: Point[] = []; + graphic: Graphics = new Graphics(); + + // 快捷切曲线绘制 + keyqListener: KeyListener = new KeyListener({ + value: 'KeyQ', + global: true, + onPress: () => { + if (this.points.length == 0) { + this.graphicTemplate.curve = true; + } + }, + }); + // 快捷切直线绘制 + keyzListener: KeyListener = new KeyListener({ + value: 'KeyZ', + global: true, + onPress: () => { + if (this.points.length == 0) { + this.graphicTemplate.curve = false; + } + }, + }); + + constructor(app: JlDrawApp, options: ILinkDrawOptions) { + super(app, new LinkTemplate(), Link.Type, '轨道Link'); + this.container.addChild(this.graphic); + this._options = options; + this.graphicTemplate.curve = true; + + new LinkPointsEditPlugin(app); + } + + newLinkData(): ILinkData { + return this._options.newData(); + } + bind(): void { + super.bind(); + this.app.addKeyboardListener(this.keyqListener, this.keyzListener); + } + unbind(): void { + super.unbind(); + this.app.removeKeyboardListener(this.keyqListener, this.keyzListener); + } + + clearCache(): void { + this.points = []; + this.graphic.clear(); + } + onRightUp(): void { + this.createAndStore(true); + } + onLeftDown(e: FederatedPointerEvent): void { + const { x, y } = this.toCanvasCoordinates(e.global); + const p = new Point(x, y); + if (this.graphicTemplate.curve) { + if (this.points.length == 0) { + this.points.push(p); + } else { + this.points.push(p, p.clone()); + } + } else { + this.points.push(p); + } + } + onLeftUp(e: FederatedMouseEvent): void { + const template = this.graphicTemplate; + if (template.curve) { + const mp = this.toCanvasCoordinates(e.global); + if (this.points.length == 1) { + this.points.push(new Point(mp.x, mp.y)); + } else if (this.points.length > 1) { + const cp2 = this.points[this.points.length - 2]; + const p = this.points[this.points.length - 1]; + cp2.copyFrom(calculateMirrorPoint(p, mp)); + this.points.push(mp); + } + } + } + + redraw(p: Point): void { + if (this.points.length < 1) return; + this.graphic.clear(); + const template = this.graphicTemplate; + this.graphic.lineStyle(template.lineWidth, new Color(template.lineColor)); + const ps = [...this.points]; + if (template.curve) { + // 曲线 + if (ps.length == 1) { + this.graphic.moveTo(ps[0].x, ps[0].y); + this.graphic.lineTo(p.x, p.y); + } else { + if ((ps.length + 1) % 3 == 0) { + ps.push(p.clone(), p.clone()); + } else { + const cp = ps[ps.length - 2]; + const p1 = ps[ps.length - 1]; + const mp = calculateMirrorPoint(p1, p); + cp.copyFrom(mp); + } + + const bps = convertToBezierPoints(ps); + bps.forEach((bp) => + this.graphic.drawBezierCurve( + bp.p1, + bp.p2, + bp.cp1, + bp.cp2, + template.segmentsCount + ) + ); + } + } else { + ps.push(p); + // 直线 + this.graphic.moveTo(ps[0].x, ps[0].y); + for (let i = 1; i < ps.length; i++) { + const p = ps[i]; + this.graphic.lineTo(p.x, p.y); + } + } + } + prepareData(): ILinkData | null { + const template = this.graphicTemplate; + if ( + (!template.curve && this.points.length < 2) || + (template.curve && this.points.length < 4) + ) { + console.log('Link绘制因点不够取消绘制'); + return null; + } + if (template.curve) { + this.points.pop(); + } + const data = this.newLinkData(); + data.id = this.nextId(); + data.graphicType = Link.Type; + data.curve = template.curve; + data.segmentsCount = template.segmentsCount; + data.points = this.points; + data.lineWidth = template.lineWidth; + data.lineColor = template.lineColor; + data.segmentsCount = template.segmentsCount; + return data; + } +} + +export class LinkGraphicHitArea implements IHitArea { + link: Link; + constructor(link: Link) { + this.link = link; + } + contains(x: number, y: number): boolean { + const p = new Point(x, y); + if (this.link.datas.curve) { + // 曲线 + const bps = convertToBezierPoints(this.link.datas.points); + for (let i = 0; i < bps.length; i++) { + const bp = bps[i]; + if ( + pointPolygon( + p, + [bp.p1, bp.cp1, bp.cp2, bp.p2], + this.link.datas.lineWidth + ) + ) { + return true; + } + } + } else { + // 直线 + for (let i = 1; i < this.link.datas.points.length; i++) { + const p1 = this.link.datas.points[i - 1]; + const p2 = this.link.datas.points[i]; + if (linePoint(p1, p2, p, this.link.datas.lineWidth)) { + return true; + } + } + } + return false; + } +} + +/** + * 构建吸附位置 + * @param link + * @returns + */ +function buildAbsorbablePositions(link: Link): AbsorbablePosition[] { + const aps: AbsorbablePosition[] = []; + const links = link.queryStore.queryByType(Link.Type); + links.forEach((other) => { + if (other.id == link.id) { + return; + } + const apa = new AbsorbablePoint( + other.localToCanvasPoint(other.getStartPoint()) + ); + const apb = new AbsorbablePoint( + other.localToCanvasPoint(other.getEndPoint()) + ); + aps.push(apa, apb); + }); + + aps.push(new AbsorbableCircle(new Point(450, 410), 30)); + + return aps; +} + +/** + * 端点拖拽添加吸附位置 + * @param g + * @param dp + * @param index + */ +function onEditPointCreate( + g: JlGraphic, + dp: DraggablePoint, + index: number +): void { + const link = g as Link; + if (index === 0 || index == link.datas.points.length - 1) { + // 端点 + dp.on('dragstart', () => { + link.getGraphicApp().setOptions({ + absorbablePositions: buildAbsorbablePositions(link), + }); + }); + } +} + +/** + * link路径编辑 + */ +export class LinkPointsEditPlugin extends GraphicInteractionPlugin { + static Name = 'LinkPointsDrag'; + constructor(app: GraphicApp) { + super(LinkPointsEditPlugin.Name, app); + } + filter(...grahpics: JlGraphic[]): Link[] | undefined { + return grahpics.filter((g) => g.type == Link.Type) as Link[]; + } + bind(g: Link): void { + g.lineGraphic.eventMode = 'static'; + g.lineGraphic.cursor = 'pointer'; + g.lineGraphic.hitArea = new LinkGraphicHitArea(g); + g.on('selected', this.onSelected, this); + g.on('unselected', this.onUnselected, this); + } + unbind(g: Link): void { + g.off('selected', this.onSelected, this); + g.off('unselected', this.onUnselected, this); + } + + onSelected(g: DisplayObject): void { + const link = g as Link; + let lep; + if (link.datas.curve) { + // 曲线 + lep = link.getAssistantAppend( + BezierCurveEditPlugin.Name + ); + if (!lep) { + lep = new BezierCurveEditPlugin( + link, + link.datas.points, + onEditPointCreate + ); + link.addAssistantAppend(lep); + } + } else { + // 直线 + lep = link.getAssistantAppend( + PolylineEditPlugin.Name + ); + if (!lep) { + lep = new PolylineEditPlugin( + link, + link.datas.points, + onEditPointCreate + ); + link.addAssistantAppend(lep); + } + } + lep.showAll(); + } + onUnselected(g: DisplayObject): void { + const link = g as Link; + let lep; + if (link.datas.curve) { + // 曲线 + lep = link.getAssistantAppend( + BezierCurveEditPlugin.Name + ); + } else { + // 直线 + lep = link.getAssistantAppend( + PolylineEditPlugin.Name + ); + } + if (lep) { + lep.hideAll(); + } + } +} diff --git a/src/graphics/turnout/Turnout.ts b/src/graphics/turnout/Turnout.ts new file mode 100644 index 0000000..4a6c49a --- /dev/null +++ b/src/graphics/turnout/Turnout.ts @@ -0,0 +1,9 @@ +import { GraphicData, JlGraphic } from 'src/jlgraphic'; + +export type ITrunoutData = GraphicData; + +export class Turnout extends JlGraphic { + doRepaint(): void { + throw new Error('Method not implemented.'); + } +} diff --git a/src/graphics/turnout/TurnoutDrawAssistant.ts b/src/graphics/turnout/TurnoutDrawAssistant.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/jlgraphic/app/JlDrawApp.ts b/src/jlgraphic/app/JlDrawApp.ts new file mode 100644 index 0000000..0d4e9ca --- /dev/null +++ b/src/jlgraphic/app/JlDrawApp.ts @@ -0,0 +1,739 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import { + BitmapFont, + BitmapText, + Container, + DisplayObject, + FederatedMouseEvent, + IPointData, + Point, +} from 'pixi.js'; +import { GraphicIdGenerator } from '../core/IdGenerator'; +import { JlGraphic, GraphicData, GraphicTemplate } from '../core/JlGraphic'; +import { BoundsGraphic, TransformPoints } from '../graphic'; +import { JlOperation } from '../operation/JlOperation'; +import { + AppInteractionPlugin, + CombinationKey, + InteractionPlugin, + KeyListener, + ViewportMovePlugin, +} from '../plugins'; +import { CommonMouseTool } from '../plugins/CommonMousePlugin'; +import { DOWN, LEFT, RIGHT, UP, recursiveChildren } from '../utils'; +import { + CanvasData, + CanvasEvent, + GraphicApp, + GraphicAppOptions, + ICanvasProperties, + JlCanvas, +} from './JlGraphicApp'; + +/** + * 图形绘制助手 + */ +export abstract class GraphicDrawAssistant< + GT extends GraphicTemplate +> extends AppInteractionPlugin { + _isDrawAssistant = true; + app: JlDrawApp; + type: string; // 图形对象类型 + description?: string; // 描述 + icon: string; // 界面显示的图标 + container: Container = new Container(); + graphicTemplate: GT; + + escListener: KeyListener = new KeyListener({ + value: 'Escape', + onPress: () => { + this.createAndStore(true); + }, + }); + + constructor( + graphicApp: JlDrawApp, + graphicTemplate: GT, + icon: string, + description?: string + ) { + super(graphicTemplate.type, graphicApp); + this.app = graphicApp; + this.graphicTemplate = graphicTemplate; + this.type = graphicTemplate.type; + this.icon = icon; + this.description = description; + this.app.registerGraphicTemplates(this.graphicTemplate); + } + + public get canvas(): JlCanvas { + return this.app.canvas; + } + + bind(): void { + const canvas = this.canvas; + canvas.addChild(this.container); + canvas.on('mousedown', this.onLeftDown, this); + canvas.on('mousemove', this.onMouseMove, this); + canvas.on('mouseup', this.onLeftUp, this); + canvas.on('rightdown', this.onRightDown, this); + canvas.on('rightup', this.onRightUp, this); + this.app.viewport.wheel({ + percent: 0.01, + }); + this.app.addKeyboardListener(this.escListener); + + this.app + .interactionPlugin(ViewportMovePlugin.Name) + .resume(); + } + unbind(): void { + this.clearCache(); + const canvas = this.canvas; + canvas.removeChild(this.container); + canvas.off('mousedown', this.onLeftDown, this); + canvas.off('mousemove', this.onMouseMove, this); + canvas.off('mouseup', this.onLeftUp, this); + canvas.off('rightdown', this.onRightDown, this); + canvas.off('rightup', this.onRightUp, this); + + this.app.viewport.plugins.remove('wheel'); + + this.app.removeKeyboardListener(this.escListener); + + this.app + .interactionPlugin(ViewportMovePlugin.Name) + .pause(); + } + + onLeftDown(e: FederatedMouseEvent) {} + + onMouseMove(e: FederatedMouseEvent) { + this.redraw(this.toCanvasCoordinates(e.global)); + } + + onLeftUp(e: FederatedMouseEvent) {} + onRightDown(e: FederatedMouseEvent) {} + onRightUp(e: FederatedMouseEvent) {} + /** + * 获取下一个id + */ + nextId(): string { + return GraphicIdGenerator.next(); + } + + clearCache(): void {} + + /** + * 重绘 + * @param cp 鼠标所在画布坐标 + */ + abstract redraw(cp: Point): void; + + abstract prepareData(): GraphicData | null; + + toCanvasCoordinates(p: Point): Point { + return this.app.toCanvasCoordinates(p); + } + /** + * 保存创建的图形对象 + */ + storeGraphic(...graphics: JlGraphic[]): void { + this.app.addGraphics(...graphics); + // 创建图形对象操作记录 + this.app.opRecord.record(new GraphicCreateOperation(this.app, graphics)); + } + /** + * 创建并添加到图形App + */ + createAndStore(finish: boolean): JlGraphic | null { + const data = this.prepareData(); + if (!data) { + if (finish) { + this.finish(); + } + return null; + } + const template = this.graphicTemplate; + const g = template.new(); + g.loadData(data); + g.id = this.nextId(); + this.storeGraphic(g); + if (finish) { + this.finish(g); + } + return g; + } + + /** + * 绘制完成 + */ + finish(...graphics: JlGraphic[]): void { + this.clearCache(); + this.app.interactionPlugin(CommonMouseTool.Name).resume(); + this.app.updateSelected(...graphics); + } +} + +/** + * 图形App事件 + */ +export enum DrawAppEvent { + propertiesupdate = 'propertiesupdate', // 图形对象数据变更 +} + +export interface IDrawAppOptions { + /** + * 绘制辅助交互插件 + */ + drawAssistants: GraphicDrawAssistant[]; +} + +export type DrawAppOptions = GraphicAppOptions & IDrawAppOptions; + +/** + * 绘制应用 + */ +export class JlDrawApp extends GraphicApp { + font: BitmapFont = BitmapFont.from( + 'coordinates', + { + fontFamily: 'Roboto', + fontSize: 16, + fill: '#ff2700', + }, + { chars: ['画布坐标:() 屏幕坐标:() 缩放:.,', ['0', '9']] } + ); + coordinates: BitmapText = new BitmapText('画布坐标: (0, 0) 屏幕坐标:(0, 0)', { + fontName: 'coordinates', + }); + scaleText: BitmapText = new BitmapText('缩放: 1', { + fontName: 'coordinates', + }); + + drawAssistants: GraphicDrawAssistant[] = []; + + selectedData: GraphicData | ICanvasProperties | null; + + constructor(dom: HTMLElement) { + super(dom); + + this.appendDrawStatesDisplay(); + + // 数据更新表单缓存处理 + this.selectedData = null; + this.initSelectedDataUpdateListen(); + // 拖拽操作记录 + this.appDragRecord(); + // 绑定通用键盘操作 + this.bindKeyboardOperation(); + } + + setOptions(options: DrawAppOptions): void { + super.setOptions(options); + // this.registerInteractionPlugin(...options.drawAssistants); + } + + registerInteractionPlugin(...plugins: InteractionPlugin[]): void { + plugins.forEach((plugin) => { + if (plugin instanceof GraphicDrawAssistant) { + this.drawAssistants.push(plugin); + } + super.registerInteractionPlugin(plugin); + }); + } + + getDrawAssistant(graphicType: string): GraphicDrawAssistant { + const sda = this.drawAssistants + .filter((da) => da.type === graphicType) + .pop(); + if (!sda) { + throw new Error(`未找到图形绘制助手: ${graphicType}`); + } + return sda; + } + + private appDragRecord() { + let dragStartDatas: GraphicData[] = []; + this.on('dragstart', () => { + // 图形拖拽,记录初始数据 + // console.log('app图形拖拽开始,记录图形数据'); + dragStartDatas = this.selectedGraphics.map((g) => g.saveData()); + }); + // 图形拖拽操作监听 + this.on('dragend', () => { + // 图形拖拽,记录操作 + // console.log('app图形拖拽结束,记录操作'); + const newData = this.selectedGraphics.map((g) => g.saveData()); + this.opRecord.record( + new DragOperation(this, this.selectedGraphics, dragStartDatas, newData) + ); + dragStartDatas = []; + }); + } + + /** + * 因图形数据更新和表单数据更新存在Vue代理及记录需求,额外处理数据的缓存 + */ + private initSelectedDataUpdateListen() { + // 画布数据更新 + this.selectedData = new CanvasData(this.canvas.properties); + this.canvas.on(CanvasEvent.propertiesupdate, () => { + if (this.selectedGraphics.length === 0) { + this.selectedData = this.canvas.saveData(); + this.emit('propertiesupdate', this.selectedData); + } + }); + this.viewport.on('moved-end', () => { + if (this.selectedGraphics.length === 0) { + this.selectedData = this.canvas.saveData(); + this.emit('propertiesupdate', this.selectedData); + } + }); + // 图形对象数据更新 + const graphicPropertiesUpdate = (g: DisplayObject) => { + if (this.selectedGraphics.length === 1) { + this.selectedData = (g as JlGraphic).saveData(); + this.emit(DrawAppEvent.propertiesupdate, this.selectedData); + } + }; + this.on('graphicselectedchange', (g: DisplayObject, selected) => { + let br = g.getAssistantAppend(BoundsGraphic.Name); + if (!br) { + // 绘制辅助包围框 + br = new BoundsGraphic(g); + } + + if (selected) { + g.on('repaint', graphicPropertiesUpdate, this); + if (br) { + br.redraw(); + br.visible = true; + } + } else { + g.off('repaint', graphicPropertiesUpdate, this); + if (br) { + br.visible = false; + } + } + if (g.scalable) { + // 缩放点 + let sp = g.getAssistantAppend(TransformPoints.Name); + if (!sp) { + sp = new TransformPoints(g); + } + if (selected) { + sp.update(); + sp.visible = true; + } else { + sp.visible = false; + } + } + if (this.selectedGraphics.length === 0) { + this.selectedData = this.canvas.properties.clone(); + } else if (this.selectedGraphics.length === 1) { + const g = this.selectedGraphics[0]; + this.selectedData = g.saveData(); + } else { + this.selectedData = null; + } + this.emit(DrawAppEvent.propertiesupdate, this.selectedData); + }); + this.on('graphicchildselectedchange', (_g, child, selected) => { + let br = child.getAssistantAppend(BoundsGraphic.Name); + if (!br) { + // 绘制辅助包围框 + br = new BoundsGraphic(child); + } + if (selected) { + br.redraw(); + br.visible = true; + } else { + br.visible = false; + } + }); + } + + /** + * 绘制状态信息显示 + */ + private appendDrawStatesDisplay(): void { + this.app.stage.addChild(this.coordinates); + this.app.stage.addChild(this.scaleText); + const bound = this.coordinates.getLocalBounds(); + this.scaleText.position.set(bound.width + 10, 0); + this.canvas.interactive = true; + this.canvas.on('mousemove', (e) => { + if (e.target) { + const { x, y } = this.toCanvasCoordinates(e.global); + const cpTxt = `(${x}, ${y})`; + const tp = e.global; + const tpTxt = `(${tp.x}, ${tp.y})`; + this.coordinates.text = `画布坐标:${cpTxt} 屏幕坐标:${tpTxt}`; + const bound = this.coordinates.getLocalBounds(); + this.scaleText.position.set(bound.width + 10, 0); + } + }); + this.viewport.on('zoomed-end', () => { + this.scaleText.text = `缩放: ${this.viewport.scaled}`; + }); + } + + bindKeyboardOperation(): void { + this.addKeyboardListener( + // Ctrl + A + new KeyListener({ + value: 'KeyA', + combinations: [CombinationKey.Ctrl], + onPress: (e: KeyboardEvent, app: GraphicApp) => { + if (e.ctrlKey) { + (app as JlDrawApp).selectAllGraphics(); + } + }, + }) + ); + + // 复制功能 + this.addKeyboardListener( + new KeyListener({ + value: 'KeyD', + combinations: [CombinationKey.Shift], + onPress: (e: KeyboardEvent, app: GraphicApp) => { + app.graphicCopyPlugin.init(); + }, + }) + ); + this.addKeyboardListener( + new KeyListener({ + // Ctrl + Z + value: 'KeyZ', + global: true, + combinations: [CombinationKey.Ctrl], + onPress: (e: KeyboardEvent, app: GraphicApp) => { + app.opRecord.undo(); + }, + }) + ); + this.addKeyboardListener( + new KeyListener({ + // Ctrl + Shift + Z + value: 'KeyZ', + global: true, + combinations: [CombinationKey.Ctrl, CombinationKey.Shift], + onPress: (e: KeyboardEvent, app: GraphicApp) => { + app.opRecord.redo(); + }, + }) + ); + + this.addKeyboardListener( + new KeyListener({ + value: 'Delete', + onPress: (e: KeyboardEvent, app: GraphicApp) => { + (app as JlDrawApp).deleteSelectedGraphics(); + }, + }) + ); + this.addKeyboardListener( + new KeyListener({ + value: 'ArrowUp', + pressTriggerEveryTime: true, + onPress: (e: KeyboardEvent, app: GraphicApp) => { + updateGraphicPositionOnKeyboardEvent(app, UP); + }, + onRelease: (e: KeyboardEvent, app: GraphicApp) => { + recordGraphicMoveOperation(app); + }, + }) + ); + this.addKeyboardListener( + new KeyListener({ + value: 'ArrowDown', + pressTriggerEveryTime: true, + onPress: (e: KeyboardEvent, app: GraphicApp) => { + updateGraphicPositionOnKeyboardEvent(app, DOWN); + }, + onRelease: (e: KeyboardEvent, app: GraphicApp) => { + recordGraphicMoveOperation(app); + }, + }) + ); + this.addKeyboardListener( + new KeyListener({ + value: 'ArrowLeft', + pressTriggerEveryTime: true, + onPress: (e: KeyboardEvent, app: GraphicApp) => { + updateGraphicPositionOnKeyboardEvent(app, LEFT); + }, + onRelease: (e: KeyboardEvent, app: GraphicApp) => { + recordGraphicMoveOperation(app); + }, + }) + ); + this.addKeyboardListener( + new KeyListener({ + value: 'ArrowRight', + pressTriggerEveryTime: true, + onPress: (e: KeyboardEvent, app: GraphicApp) => { + updateGraphicPositionOnKeyboardEvent(app, RIGHT); + }, + onRelease: (e: KeyboardEvent, app: GraphicApp) => { + recordGraphicMoveOperation(app); + }, + }) + ); + } + + /** + * 全选 + */ + selectAllGraphics() { + this.updateSelected(...this.queryStore.getAllGraphics()); + } + + /** + * 图形对象存储处理,默认添加图形交互 + * @param graphic + */ + beforeGraphicStore(graphic: JlGraphic): void { + graphic.eventMode = 'static'; + graphic.selectable = true; + graphic.draggable = true; + } + + /** + * 删除选中图形对象 + */ + deleteSelectedGraphics() { + const deletes = this.selectedGraphics.slice( + 0, + this.selectedGraphics.length + ); + this.deleteGraphics(...this.selectedGraphics); + // 删除图形对象操作记录 + this.opRecord.record(new GraphicDeleteOperation(this, deletes)); + } + + onSubmit(): void { + if (this.selectedData == null) return; + if (this.selectedGraphics.length === 0) { + const cp = this.selectedData as ICanvasProperties; + this.opRecord.record( + new UpdateCanvasOperation( + this, + this.canvas, + cp, + this.canvas.properties.clone() + ) + ); + this.canvas.update(this.selectedData as ICanvasProperties); + } else if (this.selectedGraphics.length === 1) { + const g = this.selectedGraphics[0]; + if (this.selectedData != null) { + const data = g.saveData(); + g.updateData(this.selectedData as GraphicData); + this.opRecord.record( + new UpdateDataOperation( + this, + g, + data, + this.selectedData as GraphicData + ) + ); + } + } + } +} + +let dragStartDatas: GraphicData[] = []; +function updateGraphicPositionOnKeyboardEvent(app: GraphicApp, dp: IPointData) { + let dragStart = false; + if (dragStartDatas.length === 0) { + dragStartDatas = app.selectedGraphics.map((g) => g.saveData()); + dragStart = true; + } + if ( + app.selectedGraphics.length === 1 && + app.selectedGraphics[0].hasSelectedChilds() + ) { + recursiveChildren(app.selectedGraphics[0], (child) => { + if (child.selected && child.draggable) { + child.emit('transformstart', child); + child.position.x += dp.x; + child.position.y += dp.y; + } + }); + } else { + app.selectedGraphics.forEach((g) => { + g.emit('transformstart', g); + g.position.x += dp.x; + g.position.y += dp.y; + }); + } +} +function recordGraphicMoveOperation(app: GraphicApp) { + if ( + dragStartDatas.length > 0 && + dragStartDatas.length === app.selectedGraphics.length + ) { + const newData = app.selectedGraphics.map((g) => g.saveData()); + app.opRecord.record( + new DragOperation(app, app.selectedGraphics, dragStartDatas, newData) + ); + if ( + app.selectedGraphics.length === 1 && + app.selectedGraphics[0].hasSelectedChilds() + ) { + recursiveChildren(app.selectedGraphics[0], (child) => { + if (child.selected && child.draggable) { + child.emit('transformend', child); + } + }); + } else { + app.selectedGraphics.forEach((g) => { + g.emit('transformend', g); + }); + } + } + dragStartDatas = []; +} + +/** + * 更新画布操作 + */ +export class UpdateCanvasOperation extends JlOperation { + obj: JlCanvas; + old: ICanvasProperties; + data: ICanvasProperties; + description = ''; + + constructor( + app: GraphicApp, + obj: JlCanvas, + old: ICanvasProperties, + data: ICanvasProperties + ) { + super(app, 'update-canvas'); + this.app = app; + this.obj = obj; + this.old = old; + this.data = data; + } + + undo(): JlGraphic[] { + this.obj.update(this.old); + return []; + } + redo(): JlGraphic[] { + this.obj.update(this.data); + return []; + } +} +/** + * 创建图形操作 + */ +export class GraphicCreateOperation extends JlOperation { + obj: JlGraphic[]; + description = ''; + + constructor(app: GraphicApp, obj: JlGraphic[]) { + super(app, 'graphic-create'); + this.app = app; + this.obj = obj; + } + + undo(): JlGraphic[] | void { + this.app.deleteGraphics(...this.obj); + } + redo(): JlGraphic[] { + this.app.addGraphics(...this.obj); + return this.obj; + } +} +/** + * 删除图形操作 + */ +export class GraphicDeleteOperation extends JlOperation { + obj: JlGraphic[]; + + constructor(app: GraphicApp, obj: JlGraphic[]) { + super(app, 'graphic-delete'); + this.app = app; + this.obj = obj; + } + + undo(): JlGraphic[] { + this.app.addGraphics(...this.obj); + return this.obj; + } + redo(): void { + this.app.deleteGraphics(...this.obj); + } +} + +/** + * 更新数据操作 + */ +export class UpdateDataOperation extends JlOperation { + obj: JlGraphic; + data: GraphicData; // 更新为data的protobuf数据 + old: GraphicData; // 更新前data的protobuf数据 + description = ''; + + constructor( + app: GraphicApp, + obj: JlGraphic, + old: GraphicData, + data: GraphicData + ) { + super(app, 'update-payload'); + this.app = app; + this.obj = obj; + this.old = old; + this.data = data; + } + + undo(): JlGraphic[] { + this.obj.updateData(this.old); + return [this.obj]; + } + redo(): JlGraphic[] { + this.obj.updateData(this.data); + return [this.obj]; + } +} + +export class DragOperation extends JlOperation { + obj: JlGraphic[]; + oldData: GraphicData[]; + newData: GraphicData[]; + constructor( + app: GraphicApp, + obj: JlGraphic[], + oldData: GraphicData[], + newData: GraphicData[] + ) { + super(app, 'graphic-drag'); + this.obj = [...obj]; + this.oldData = oldData; + this.newData = newData; + } + + undo(): void | JlGraphic[] { + for (let i = 0; i < this.obj.length; i++) { + const g = this.obj[i]; + g.exitChildEdit(); + g.updateData(this.oldData[i]); + } + return this.obj; + } + redo(): void | JlGraphic[] { + for (let i = 0; i < this.obj.length; i++) { + const g = this.obj[i]; + g.exitChildEdit(); + g.updateData(this.newData[i]); + } + return this.obj; + } +} diff --git a/src/jlgraphic/app/JlGraphicApp.ts b/src/jlgraphic/app/JlGraphicApp.ts new file mode 100644 index 0000000..f204913 --- /dev/null +++ b/src/jlgraphic/app/JlGraphicApp.ts @@ -0,0 +1,880 @@ +import EventEmitter from 'eventemitter3'; +import { Viewport } from 'pixi-viewport'; +import { + Application, + Color, + Container, + DisplayObject, + Graphics, + Point, + Rectangle, +} from 'pixi.js'; +import { GraphicQueryStore, GraphicStore } from '../core/GraphicStore'; +import { GraphicIdGenerator } from '../core/IdGenerator'; +import { + JlGraphic, + GraphicData, + GraphicState, + GraphicTemplate, + GraphicTransform, +} from '../core/JlGraphic'; +import { AbsorbablePosition } from '../graphic'; +import { + AppWsMsgBroker, + StompCli, + type AppStateSubscription, + type StompCliOption, +} from '../message/WsMsgBroker'; +import { OperationRecord } from '../operation/JlOperation'; +import { + CommonMouseTool, + GraphicDragEvent, + IMouseToolOptions, +} from '../plugins'; +import { GraphicCopyPlugin } from '../plugins/CopyPlugin'; +import { + InteractionPlugin, + InteractionPluginType, + ViewportMovePlugin, +} from '../plugins/InteractionPlugin'; +import { + JlGraphicAppKeyboardPlugin, + KeyListener, +} from '../plugins/KeyboardPlugin'; +import { ContextMenuPlugin } from '../ui/ContextMenu'; +import { getRectangleCenter, recursiveChildren } from '../utils/GraphicUtils'; + +export const AppConsts = { + viewportname: '__viewport', + canvasname: '__jlcanvas', + AssistantAppendsName: '__assistantAppends', +}; + +/** + * 画布属性 + */ +export interface ICanvasProperties { + width: number; + height: number; + backgroundColor: string; + viewportTransform: GraphicTransform; +} + +export class CanvasData implements ICanvasProperties { + width: number; + height: number; + backgroundColor: string; + viewportTransform: GraphicTransform; + constructor( + properties: ICanvasProperties = { + width: 1920, + height: 1080, + backgroundColor: '#ffffff', + viewportTransform: GraphicTransform.default(), + } + ) { + this.width = properties.width; + this.height = properties.height; + this.backgroundColor = properties.backgroundColor; + this.viewportTransform = properties.viewportTransform; + } + + copyFrom(properties: ICanvasProperties): void { + this.width = properties.width; + this.height = properties.height; + this.backgroundColor = properties.backgroundColor; + this.viewportTransform = properties.viewportTransform; + } + + clone(): CanvasData { + const cp = new CanvasData(this); + return cp; + } +} + +export enum CanvasEvent { + canvassizechange = 'canvassizechange', + propertiesupdate = 'propertiesupdate', + enterAbsorbableArea = 'enter-absorbable-area', + outAbsorbableArea = 'out-absorbable-area', +} + +export class JlCanvas extends Container { + __JlCanvas = true; + type = 'Canvas'; + app: GraphicApp; + _properties: CanvasData; + bg: Graphics = new Graphics(); // 背景 + nonInteractiveContainer: Container; // 无交互对象容器 + assistantAppendContainer: Container; // 辅助附加容器 + + constructor(app: GraphicApp, properties: CanvasData = new CanvasData()) { + super(); + this.app = app; + this._properties = properties; + this.eventMode = 'static'; + this.nonInteractiveContainer = new Container(); + this.nonInteractiveContainer.name = 'non-interactives'; + this.nonInteractiveContainer.eventMode = 'none'; + this.addChild(this.bg); + this.addChild(this.nonInteractiveContainer); + + this.sortableChildren = true; + this.assistantAppendContainer = new Container(); + this.assistantAppendContainer.eventMode = 'static'; + this.assistantAppendContainer.name = AppConsts.AssistantAppendsName; + this.assistantAppendContainer.zIndex = 999; + this.assistantAppendContainer.sortableChildren = true; + this.addChild(this.assistantAppendContainer); + + this.repaint(); + } + + /** + * 图形重绘(数据/状态变化时触发) + */ + repaint(): void { + this.doRepaint(); + } + + public get width(): number { + return this._properties.width; + } + + public get height(): number { + return this._properties.height; + } + + public get backgroundColor(): string { + return this._properties.backgroundColor; + } + + doRepaint() { + this.bg.clear(); + this.bg + .beginFill(new Color(this.backgroundColor)) + .drawRect(0, 0, this._properties.width, this._properties.height) + .endFill(); + } + + public get properties(): CanvasData { + return this._properties; + } + + saveData(): CanvasData { + const vp = this.getViewport(); + this.properties.viewportTransform = vp.saveTransform(); + return this.properties.clone(); + } + + update(properties: ICanvasProperties) { + // 更新画布 + if ( + this.properties.width !== properties.width || + this.properties.height !== properties.height + ) { + this._properties.copyFrom(properties); + this.emit(CanvasEvent.canvassizechange, this); + } else { + this._properties.copyFrom(properties); + } + this.repaint(); + const vp = this.getViewport(); + vp.loadTransform(properties.viewportTransform); + this.emit(CanvasEvent.propertiesupdate, this); + } + + addChild(...children: U): U[0] { + const rt = super.addChild(...children); + children.forEach((g) => { + recursiveChildren(g as Container, (child) => child.onAddToCanvas()); + }); + return rt; + } + removeChild(...children: U): U[0] { + children.forEach((g) => { + recursiveChildren(g as Container, (child) => child.onRemoveFromCanvas()); + }); + return super.removeChild(...children); + } + /** + * 添加无交互Child + */ + addNonInteractiveChild(...obj: DisplayObject[]): void { + this.nonInteractiveContainer.addChild(...obj); + obj.forEach((g) => { + recursiveChildren(g as Container, (child) => child.onAddToCanvas()); + }); + } + + removeGraphic(...obj: DisplayObject[]): void { + obj.forEach((g) => { + recursiveChildren(g as Container, (child) => child.onRemoveFromCanvas()); + }); + this.removeChild(...obj); + this.nonInteractiveContainer.removeChild(...obj); + } + + /** + * 移除无交互Child + */ + removeNonInteractiveChild(...obj: DisplayObject[]): void { + obj.forEach((g) => { + recursiveChildren(g as Container, (child) => child.onRemoveFromCanvas()); + }); + this.nonInteractiveContainer.removeChild(...obj); + } + + addAssistantAppends(...appends: DisplayObject[]): void { + this.assistantAppendContainer.addChild(...appends); + appends.forEach((g) => { + recursiveChildren(g as Container, (child) => child.onAddToCanvas()); + }); + } + removeAssistantAppends(...appends: DisplayObject[]): void { + appends.forEach((g) => { + recursiveChildren(g as Container, (child) => child.onAddToCanvas()); + }); + this.assistantAppendContainer.removeChild(...appends); + } + + /** + * 暂停所有可交互对象 + */ + pauseInteractiveChildren() { + this.interactiveChildren = false; + } + + /** + * 恢复所有可交互对象 + */ + resumeInteractiveChildren() { + this.interactiveChildren = true; + } +} + +/** + * 选中改变事件 + */ +export class SelectedChangeEvent { + graphic: JlGraphic; + select: boolean; + + constructor(graphic: JlGraphic, select: boolean) { + this.graphic = graphic; + this.select = select; + } +} + +export interface GraphicAppEvents extends GlobalMixins.GraphicAppEvents { + graphicstored: [graphic: JlGraphic]; + graphicdeleted: [graphic: JlGraphic]; + loadfinish: []; + 'interaction-plugin-resume': [activeTool: InteractionPlugin]; // 交互插件启用 + 'interaction-plugin-pause': [activeTool: InteractionPlugin]; // 交互插件停止 + 'options-update': [options: GraphicAppOptions]; // 配置更新 + graphicselectedchange: [graphic: JlGraphic, selected: boolean]; + graphicchildselectedchange: [ + graphic: JlGraphic, + child: DisplayObject, + selected: boolean + ]; + scaleend: [obj: DisplayObject]; + dragstart: [event: GraphicDragEvent]; + dragmove: [event: GraphicDragEvent]; + dragend: [event: GraphicDragEvent]; + destroy: [app: GraphicApp]; +} + +/** + * 图形App构造参数 + */ +export interface IGraphicAppConfig { + /** + * 交互类型配置 + */ + interactiveTypeOptions?: IInteractiveGraphicOptions; + /** + * 最大保存的操作记录数,默认100,越大越占用内存资源 + */ + maxOperationRecords?: number; + /** + * 通用鼠标工具选项 + */ + mouseToolOptions?: IMouseToolOptions; + /** + * 可吸附位置列表 + */ + absorbablePositions?: AbsorbablePosition[]; + /** + * 超出屏幕显示范围是否剪裁,默认true + */ + cullable?: boolean; +} + +/** + * 图形添加到容器选项 + */ +export interface IInteractiveGraphicOptions { + /** + * 包含添加到可交互容器的图形类型,和Excludes同时只能存在一个 + */ + interactiveGraphicTypeIncludes?: string[]; + /** + * 不包含添加到可交互容器的图形类型,和Includes同时只能存在一个 + */ + interactiveGraphicTypeExcludes?: string[]; +} + +export type GraphicAppOptions = IGraphicAppConfig; + +/** + * 图形app基类 + */ +export class GraphicApp extends EventEmitter { + private graphicStore: GraphicStore; + _options?: GraphicAppOptions; + dom: HTMLElement; + app: Application; // Pixi 渲染器 + viewport: Viewport; // 视口 + canvas: JlCanvas; // 画布 + interactiveTypeOptions: IInteractiveGraphicOptions; + + graphicTemplateMap: Map = new Map< + string, + GraphicTemplate + >(); // 图形对象模板 + + opRecord: OperationRecord; // 操作记录 + + keyboardPlugin: JlGraphicAppKeyboardPlugin; // 键盘操作处理插件 + graphicCopyPlugin: GraphicCopyPlugin; // 图形复制操作插件 + menuPlugin: ContextMenuPlugin; // 菜单插件 + + interactionPluginMap: Map = new Map< + string, + InteractionPlugin + >(); // 交互插件 + + wsMsgBroker?: AppWsMsgBroker; + + constructor(dom: HTMLElement) { + super(); + document.body.style.overflow = 'hidden'; + // console.log('创建图形App') + this.dom = dom; + this.graphicStore = new GraphicStore(this); + /** + * 默认都添加为非交互 + */ + this.interactiveTypeOptions = { interactiveGraphicTypeIncludes: [] }; + // 创建pixi渲染app + this.app = new Application({ + width: dom.clientWidth, + height: dom.clientHeight, + antialias: true, + resizeTo: window, + }); + dom.appendChild(this.app.view as unknown as Node); + + // 创建画布 + this.canvas = new JlCanvas(this); + this.canvas.name = AppConsts.canvasname; + // 创建相机 + this.viewport = new Viewport({ + screenWidth: window.innerWidth, + screenHeight: window.innerHeight, + worldWidth: this.canvas._properties.width, + worldHeight: this.canvas._properties.height, + // divWheel: dom, + passiveWheel: true, + events: this.app.renderer.events, + disableOnContextMenu: true, + }); + // 设置视口操作方式 + this.viewport + .wheel({ + percent: 1, + }) + .pinch() + .clampZoom({ + minScale: 0.1, + maxScale: 8, + }) + .clamp({ + direction: 'all', + }); + this.viewport.name = AppConsts.viewportname; + this.viewport.interactiveChildren = true; + // 添加视口到渲染器舞台 + this.app.stage.addChild(this.viewport); + // 将画布置于视口 + this.viewport.addChild(this.canvas); + + // 监听并通知缩放变化事件 + this.viewport.on('zoomed-end', () => { + this.emit('scaleend', this.viewport); + }); + + this.canvas.on(CanvasEvent.canvassizechange, () => { + this.updateViewport(); + }); + + this.opRecord = new OperationRecord(); + + // 绑定键盘监听 + this.keyboardPlugin = new JlGraphicAppKeyboardPlugin(this); + this.graphicCopyPlugin = new GraphicCopyPlugin(this); + this.menuPlugin = new ContextMenuPlugin(this); + + // 添加通用交互插件 + const tool = new CommonMouseTool(this); + tool.resume(); + // 视口移动插件 + ViewportMovePlugin.new(this); + } + + setOptions(options: GraphicAppOptions) { + // console.log('更新选项', options); + if (this._options) { + this._options = Object.assign(this._options, options); + } else { + this._options = options; + } + if (options.interactiveTypeOptions) { + // 更新交互类型配置 + this.interactiveTypeOptions = options.interactiveTypeOptions; + } + if (options.maxOperationRecords && options.maxOperationRecords > 0) { + this.opRecord.setMaxLen(options.maxOperationRecords); + } + this.emit('options-update', options); + } + + /** + * 注册图形对象模板 + * @param graphicTemplates + */ + registerGraphicTemplates(...graphicTemplates: GraphicTemplate[]) { + graphicTemplates.forEach((graphicTemplate) => { + this.graphicTemplateMap.set(graphicTemplate.type, graphicTemplate); + // 加载资源 + graphicTemplate.loadAsserts(); + }); + } + + getGraphicTemplatesByType(type: string): GT { + const template = this.graphicTemplateMap.get(type); + if (!template) { + throw new Error(`不存在type=${type}的图形对象模板`); + } + return template as GT; + } + + /** + * 使能websocket Stomp通信 + */ + enableWsStomp(options: StompCliOption) { + StompCli.new(options); + this.wsMsgBroker = new AppWsMsgBroker(this); + } + + /** + * 订阅websocket消息 + */ + subscribe(sub: AppStateSubscription) { + if (this.wsMsgBroker) { + // console.log('APP订阅', sub) + this.wsMsgBroker.subscribe(sub); + } else { + throw new Error('请先打开StompClient, 执行app.enableWebsocket()'); + } + } + /** + * 取消websocket订阅 + */ + unsubscribe(destination: string) { + if (this.wsMsgBroker) { + this.wsMsgBroker.unsbuscribe(destination); + } else { + throw new Error('请先执行enableWebsocket'); + } + } + + /** + * 处理websocket状态 + * @param graphicStates + */ + handleGraphicStates(graphicStates: GraphicState[]) { + graphicStates.forEach((state) => { + const list = this.queryStore.queryByIdOrCode(state.code); + if (list.length == 0) { + const template = this.getGraphicTemplatesByType(state.graphicType); + const g = template.new(); + g.loadState(state); + this.addGraphics(g); + } else { + list.forEach((g) => { + g.updateStates(state); + }); + } + }); + } + + /** + * 添加键盘监听器,如果是相同的按键,新注册的会覆盖旧的,当移除新的时,旧的自动生效 + * @param keyListeners + */ + addKeyboardListener(...keyListeners: KeyListener[]) { + keyListeners.forEach((keyListener) => + this.keyboardPlugin.addKeyListener(keyListener) + ); + } + + /** + * 移除键盘监听器 + * @param keyListeners + */ + removeKeyboardListener(...keyListeners: KeyListener[]) { + keyListeners.forEach((keyListener) => + this.keyboardPlugin.removeKeyListener(keyListener) + ); + } + + /** + * dom尺寸变更处理 + * @param width canvas容器的宽 + * @param height canvas容器的高 + */ + onDomResize(width: number, height: number) { + this.updateViewport(width, height); + } + + public get queryStore(): GraphicQueryStore { + return this.graphicStore; + } + + public get selectedGraphics(): JlGraphic[] { + return this.queryStore.getAllGraphics().filter((g) => g.selected); + } + + fireSelectedChange(graphic: JlGraphic) { + // console.log('通知选中变化', this.selecteds) + const select = graphic.selected; + this.emit('graphicselectedchange', graphic, select); + } + + /** + * 更新选中 + */ + updateSelected(...graphics: JlGraphic[]) { + this.selectedGraphics.forEach((graphic) => { + if (graphics.findIndex((g) => g.id === graphic.id) >= 0) { + return; + } + if (graphic.selected) { + graphic.updateSelected(false); + this.fireSelectedChange(graphic); + } + }); + graphics.forEach((graphic) => { + if (graphic.updateSelected(true)) { + this.fireSelectedChange(graphic); + } + }); + } + + /** + * 更新画布 + * @param param + */ + updateCanvas(param: ICanvasProperties) { + this.canvas.update(param); + } + + /** + * 加载图形,GraphicApp默认添加到无交互容器,DrawApp默认添加到交互容器,如需定制,提供选项配置 + * @param protos + * @param options 添加到可交互/不可交互容器选项配置 + */ + loadGraphic(protos: GraphicData[]) { + // console.log('开始加载proto数据', protos); + // 加载数据到图形存储 + protos.forEach((proto) => { + const template = this.getGraphicTemplatesByType(proto.graphicType); + const g = template.new(); + g.loadData(proto); + this.addGraphics(g); + }); + // 加载数据关系 + this.graphicStore.getAllGraphics().forEach((g) => g.loadRealtions()); + // 更新id生成器 + const max = + this.graphicStore + .getAllGraphics() + .filter((g) => !isNaN(parseInt(g.id))) + .map((g) => parseInt(g.id)) + .sort((a, b) => a - b) + .pop() ?? 0; + // console.log('最大值', max) + GraphicIdGenerator.initSerial(max); + // 加载完成通知 + this.emit('loadfinish'); + } + + /** + * 添加图形前处理 + * @param graphic + */ + beforeGraphicStore(graphic: JlGraphic): void { + const options = this.interactiveTypeOptions; + // 默认无交互 + graphic.eventMode = 'auto'; + if (options) { + if ( + options.interactiveGraphicTypeIncludes && + options.interactiveGraphicTypeIncludes.findIndex( + (type) => type === graphic.type + ) >= 0 + ) { + graphic.eventMode = 'static'; + } else if ( + options.interactiveGraphicTypeExcludes && + options.interactiveGraphicTypeExcludes.findIndex( + (type) => type === graphic.type + ) < 0 + ) { + graphic.eventMode = 'static'; + } + } + } + private doAddGraphics(graphic: JlGraphic): void { + this.beforeGraphicStore(graphic); + if (this.graphicStore.storeGraphics(graphic)) { + // cullable,默认设置剪裁,如果图形包围框不在屏幕内,则不渲染,增加效率用 + if (!this._options || this._options.cullable !== false) { + graphic.cullable = true; + } + if (graphic.eventMode == 'static' || graphic.eventMode == 'dynamic') { + // 添加为可交互 + // console.log(`type=${graphic.type}的图形添加到交互容器`); + this.canvas.addChild(graphic); + } else { + // 添加到不可交互容器 + // console.log(`type=${graphic.type}的图形添加到无交互容器`); + this.canvas.addNonInteractiveChild(graphic); + } + graphic.repaint(); + this.emit('graphicstored', graphic); + graphic.on('childselected', (child) => { + this.emit('graphicchildselectedchange', graphic, child, true); + }); + graphic.on('childunselected', (child) => { + this.emit('graphicchildselectedchange', graphic, child, false); + }); + } + } + + private doDeleteGraphics(graphic: JlGraphic): void { + // graphic可能是vue的Proxy对象,会导致canvas删除时因不是同一个对象而无法从画布移除 + const g = this.graphicStore.deleteGraphics(graphic); + if (g) { + // 从画布移除 + this.canvas.removeGraphic(g); + // 清除选中 + if (g.updateSelected(false)) { + this.fireSelectedChange(g); + } + // 对象删除处理 + g.onDelete(); + this.emit('graphicdeleted', g); + } + } + + /** + * 存储图形 + * @param graphics 图形对象 + */ + addGraphics(...graphics: JlGraphic[]) { + graphics.forEach((g) => this.doAddGraphics(g)); + } + /** + * 删除图形 + * @param graphics 图形对象 + */ + deleteGraphics(...graphics: JlGraphic[]) { + graphics.forEach((g) => this.doDeleteGraphics(g)); + } + + /** + * 检测并构建关系 + */ + detectRelations(): void { + this.graphicStore.getAllGraphics().forEach((g) => g.buildRelation()); + } + + /** + * 转换操作坐标点为画布坐标点 + * @param e 事件 + * @returns 画布坐标点 + */ + toCanvasCoordinates(p: Point): Point { + return this.viewport.toWorld(p); + } + + /** + * 获取当前缩放倍率 + */ + getViewportScaled(): number { + return this.viewport.scaled; + } + /** + * 视口中心坐标 + * @returns + */ + getViewportCenter(): Point { + return this.viewport.center; + } + /** + * 获取视口角落坐标 + * @returns + */ + getViewportCorner(): Point { + return this.viewport.corner; + } + + /** + * 注册交互插件,会替换旧的 + */ + registerInteractionPlugin(...plugins: InteractionPlugin[]): void { + plugins.forEach((plugin) => { + const old = this.interactionPluginMap.get(plugin.name); + if (old) { + console.warn(`已经存在name=${plugin.name}的交互插件,忽略此插件注册!`); + return; + } + this.interactionPluginMap.set(plugin.name, plugin); + }); + } + + /** + * 根据名称获取交互插件 + * @param name + * @returns + */ + interactionPlugin

(name: string): P { + const plugin = this.interactionPluginMap.get(name); + if (!plugin) { + throw new Error(`未找到name=${name}的交互插件`); + } + return plugin as P; + } + + /** + * 停止应用交互插件 + */ + pauseAppInteractionPlugins(): void { + this.interactionPluginMap.forEach((plugin) => { + if (plugin.isActive() && plugin._type === InteractionPluginType.App) { + this.doPauseInteractionPlugin(plugin); + } + }); + } + + private doPauseInteractionPlugin(plugin?: InteractionPlugin): void { + if (plugin && plugin.isActive()) { + plugin.pause(); + this.emit('interaction-plugin-pause', plugin); + } + } + + /** + * 移除交互插件 + */ + removeInteractionPlugin(plugin: InteractionPlugin) { + this.interactionPluginMap.delete(plugin.name); + } + + private updateViewport(domWidth?: number, domHeight?: number): void { + let screenWidth = this.viewport.screenWidth; + let screenHeight = this.viewport.screenHeight; + if (domWidth) { + screenWidth = domWidth; + } + if (domHeight) { + screenHeight = domHeight; + } + const worldWidth = this.canvas._properties.width; + const worldHeight = this.canvas._properties.height; + this.viewport.resize(screenWidth, screenHeight, worldWidth, worldHeight); + if (this.viewport.OOB().right) { + this.viewport.right = this.viewport.right + 1; + } else if (this.viewport.OOB().left) { + this.viewport.left = this.viewport.left - 1; + } else if (this.viewport.OOB().top) { + this.viewport.top = this.viewport.top - 1; + } else if (this.viewport.OOB().bottom) { + this.viewport.bottom = this.viewport.bottom + 1; + } + } + + /** + * 使图形居中显示(所有图形的外包围盒) + */ + makeGraphicCenterShow(...group: JlGraphic[]): void { + if (group.length > 0) { + const bounds0 = group[0].getBounds(); + let lx = bounds0.x; + let ly = bounds0.y; + let rx = bounds0.x + bounds0.width; + let ry = bounds0.y + bounds0.height; + if (group.length > 1) { + for (let i = 1; i < group.length; i++) { + const g = group[i]; + const bound = g.getBounds(); + if (bound.x < lx) { + lx = bound.x; + } + if (bound.y < ly) { + ly = bound.y; + } + const brx = bound.x + bound.width; + if (brx > rx) { + rx = brx; + } + const bry = bound.y + bound.height; + if (bry > ry) { + ry = bry; + } + } + } + const { x, y } = getRectangleCenter( + new Rectangle(lx, ly, rx - lx, ry - ly) + ); + const p = this.viewport.toWorld(x, y); + this.viewport.moveCenter(p.x, p.y); + } + } + + /** + * 销毁 + */ + destroy(): void { + console.log('销毁图形 APP'); + this.emit('destroy', this); + if (this.wsMsgBroker) { + this.wsMsgBroker.close(); + if (!StompCli.hasAppMsgBroker()) { + // 如果没有其他消息代理,关闭websocket Stomp客户端 + StompCli.close(); + } + } + this.interactionPluginMap.forEach((plugin) => { + plugin.destroy(); + }); + this.canvas.destroy(true); + this.viewport.destroy(); + this.app.destroy(true, true); + document.body.style.overflow = 'auto'; + } +} diff --git a/src/jlgraphic/app/index.ts b/src/jlgraphic/app/index.ts new file mode 100644 index 0000000..9e8a44a --- /dev/null +++ b/src/jlgraphic/app/index.ts @@ -0,0 +1,2 @@ +export * from './JlGraphicApp'; +export * from './JlDrawApp'; diff --git a/src/jlgraphic/core/GraphicRelation.ts b/src/jlgraphic/core/GraphicRelation.ts new file mode 100644 index 0000000..88375d5 --- /dev/null +++ b/src/jlgraphic/core/GraphicRelation.ts @@ -0,0 +1,185 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { GraphicApp } from '../app/JlGraphicApp'; +import { JlGraphic } from './JlGraphic'; + +/** + * 图形关系数据 + */ +export class GraphicRelationParam { + g: JlGraphic; + param: any; + constructor(g: JlGraphic, param: any = null) { + this.g = g; + this.param = param; + } + isTheGraphic(g: JlGraphic): boolean { + return this.g.id === g.id; + } + getGraphic(): G { + return this.g as G; + } + getParam

(): P { + return this.param as P; + } + equals(other: GraphicRelationParam): boolean { + return this.isTheGraphic(other.g) && this.param === other.param; + } +} +/** + * 图形关系 + */ +export class GraphicRelation { + rp1: GraphicRelationParam; + rp2: GraphicRelationParam; + constructor(rp1: GraphicRelationParam, rp2: GraphicRelationParam) { + this.rp1 = rp1; + this.rp2 = rp2; + } + + contains(g: JlGraphic): boolean { + return this.rp1.isTheGraphic(g) || this.rp2.isTheGraphic(g); + } + + /** + * 获取给定图形的关系参数 + * @param g + * @returns + */ + getRelationParam(g: JlGraphic): GraphicRelationParam { + if (!this.contains(g)) { + throw new Error( + `图形关系${this.rp1.g.id}-${this.rp2.g.id}中不包含给定图形${g.id}` + ); + } + if (this.rp1.isTheGraphic(g)) { + return this.rp1; + } else { + return this.rp2; + } + } + /** + * 获取关联的另一个图形的关系参数 + * @param g + * @returns + */ + getOtherRelationParam(g: JlGraphic): GraphicRelationParam { + if (!this.contains(g)) { + throw new Error( + `图形关系${this.rp1.g.id}-${this.rp2.g.id}中不包含给定图形${g.id}` + ); + } + if (this.rp1.isTheGraphic(g)) { + return this.rp2; + } else { + return this.rp1; + } + } + /** + * 获取关联的另一个图形对象 + * @param g + * @returns graphic + */ + getOtherGraphic(g: JlGraphic): G { + return this.getOtherRelationParam(g).g as G; + } + + equals(orp1: GraphicRelationParam, orp2: GraphicRelationParam): boolean { + if (this.rp1.isTheGraphic(orp1.g)) { + return this.rp1.equals(orp1) && this.rp2.equals(orp2); + } else if (this.rp1.isTheGraphic(orp2.g)) { + return this.rp1.equals(orp2) && this.rp2.equals(orp1); + } + return false; + } + + isEqualOther(other: GraphicRelation): boolean { + return this.equals(other.rp1, other.rp2); + } +} + +/** + * 图形关系管理 + */ +export class RelationManage { + app: GraphicApp; + relations: GraphicRelation[] = []; + constructor(app: GraphicApp) { + this.app = app; + } + + isContainsRelation( + rp1: GraphicRelationParam, + rp2: GraphicRelationParam + ): boolean { + const relation = this.relations.find((relation) => + relation.equals(rp1, rp2) + ); + return !!relation; + } + addRelation( + rp1: GraphicRelationParam | JlGraphic, + rp2: GraphicRelationParam | JlGraphic + ): void { + if (!(rp1 instanceof GraphicRelationParam)) { + rp1 = new GraphicRelationParam(rp1); + } + if (!(rp2 instanceof GraphicRelationParam)) { + rp2 = new GraphicRelationParam(rp2); + } + if (!this.isContainsRelation(rp1, rp2)) { + const relation = new GraphicRelation(rp1, rp2); + this.relations.push(relation); + } + } + + /** + * 获取图形的所有关系 + * @param g + * @returns + */ + getRelationsOfGraphic(g: JlGraphic): GraphicRelation[] { + return this.relations.filter((rl) => rl.contains(g)); + } + + /** + * 获取指定图形的指定关系图形类型的所有关系 + * @param g 指定图形 + * @param type 关联图形的类型 + * @returns + */ + getRelationsOfGraphicAndOtherType( + g: JlGraphic, + type: string + ): GraphicRelation[] { + return this.relations.filter( + (rl) => rl.contains(g) && rl.getOtherGraphic(g).type === type + ); + } + + /** + * 删除关系 + * @param relation + */ + private deleteRelation(relation: GraphicRelation): void { + const index = this.relations.findIndex((rl) => rl.isEqualOther(relation)); + if (index >= 0) { + this.relations.splice(index, 1); + } + } + /** + * 删除指定图形的所有关系 + * @param g + */ + deleteRelationOfGraphic(g: JlGraphic): void { + const relations = this.getRelationsOfGraphic(g); + relations.forEach((rl) => this.deleteRelation(rl)); + } + /** + * 删除指定图形的所有关系 + * @param g + */ + deleteRelationOfGraphicAndOtherType(g: JlGraphic, type: string): void { + const relations = this.getRelationsOfGraphicAndOtherType(g, type); + relations.forEach((rl) => this.deleteRelation(rl)); + } +} diff --git a/src/jlgraphic/core/GraphicStore.ts b/src/jlgraphic/core/GraphicStore.ts new file mode 100644 index 0000000..979bd20 --- /dev/null +++ b/src/jlgraphic/core/GraphicStore.ts @@ -0,0 +1,185 @@ +import { GraphicApp } from '../app/JlGraphicApp'; +import { RelationManage } from './GraphicRelation'; +import { JlGraphic } from './JlGraphic'; + +export interface GraphicQueryStore { + /** + * 获取所有图形对象 + */ + getAllGraphics(): JlGraphic[]; + /** + * 根据id获取图形 + */ + queryById(id: string): T; + /** + * 根据id模糊查询图形 + * @param id + */ + queryByIdAmbiguous(id: string): JlGraphic[]; + + /** + * 根据类型获取图形列表 + */ + queryByType(type: string): T[]; + /** + * 根据code查询 + * @param code + */ + queryByCode(code: string): JlGraphic[] | undefined; + /** + * 根据code模糊查询图形 + * @param code + * @param type + */ + queryByCodeAmbiguous(code: string): JlGraphic[]; + /** + * 根据id或code查询图形 + * @param v + */ + queryByIdOrCode(v: string): JlGraphic[]; + /** + * 根据code和类型获取图形 + * @param code + * @param type + */ + queryByCodeAndType( + code: string, + type: string + ): T | undefined; + /** + * 根据code和类型模糊查询图形 + * @param code + * @param type + */ + queryByCodeAndTypeAmbiguous( + code: string, + type: string + ): T[]; +} + +/** + * 图形存储 + */ +export class GraphicStore implements GraphicQueryStore { + app: GraphicApp; + store: Map; + relationManage: RelationManage; + constructor(app: GraphicApp) { + this.app = app; + this.store = new Map(); + this.relationManage = new RelationManage(app); + } + /** + * 获取所有图形对象 + */ + getAllGraphics(): JlGraphic[] { + return [...this.store.values()]; + } + queryById(id: string): T { + const graphic = this.store.get(id) as T; + if (!graphic) throw Error(`未找到id为 [${id}] 的图形.`); + return this.store.get(id) as T; + } + queryByIdAmbiguous(id: string): JlGraphic[] { + const list: JlGraphic[] = []; + this.store.forEach((g) => { + if (g.id.search(id) >= 0) { + list.push(g); + } + }); + return list; + } + queryByType(type: string): T[] { + const list: T[] = []; + this.store.forEach((g) => { + if (g.type === type) { + list.push(g as T); + } + }); + return list; + } + queryByCode(code: string): JlGraphic[] | undefined { + const list: JlGraphic[] = []; + this.store.forEach((g) => { + if (g.code === code) { + list.push(g); + } + }); + return list; + } + queryByCodeAmbiguous(code: string): JlGraphic[] { + const list: JlGraphic[] = []; + this.store.forEach((g) => { + if (g.code.search(code) >= 0) { + list.push(g); + } + }); + return list; + } + queryByIdOrCode(s: string): JlGraphic[] { + const list: JlGraphic[] = []; + this.store.forEach((g) => { + if (g.isIdOrCode(s)) { + list.push(g); + } + }); + return list; + } + queryByCodeAndType( + code: string, + type: string + ): T | undefined { + for (const item of this.store.values()) { + if (item.code === code && item.type === type) { + return item as T; + } + } + } + queryByCodeAndTypeAmbiguous( + code: string, + type: string + ): T[] { + const list: T[] = []; + this.store.forEach((g) => { + if (g.type === type && g.code.search(code) >= 0) { + list.push(g as T); + } + }); + return list; + } + + /** + * 存储图形对象 + * @param graphics 要存储的图形 + */ + storeGraphics(graphic: JlGraphic): boolean { + if (!graphic.id || graphic.id.trim() === '') { + throw new Error(`存储图形对象异常: id为空, ${graphic}`); + } + if (this.store.has(graphic.id)) { + // 已经存在 + const exist = this.store.get(graphic.id); + console.error(`已经存在id=${graphic.id}的设备${exist}`); + return false; + } else { + this.store.set(graphic.id, graphic); + graphic.queryStore = this; + graphic.relationManage = this.relationManage; + return true; + } + } + /** + * 删除图形 + * @param graph 要删除的图形 + */ + deleteGraphics(graphic: JlGraphic): JlGraphic | undefined { + const id = graphic.id; + const remove = this.store.get(id); + if (remove) { + this.store.delete(id); + // 删除图形关系 + this.relationManage.deleteRelationOfGraphic(remove); + } + return remove; + } +} diff --git a/src/jlgraphic/core/IdGenerator.ts b/src/jlgraphic/core/IdGenerator.ts new file mode 100644 index 0000000..131f699 --- /dev/null +++ b/src/jlgraphic/core/IdGenerator.ts @@ -0,0 +1,28 @@ +/** + * ID生成器 + */ +export class IdGenerator { + serial = 0; + type: string; + + constructor(type: string) { + this.type = type; + } + + next(): string { + ++this.serial; + // console.log(this.getType() + this.serial) + return this.getType() + this.serial; + } + + getType(): string { + return this.type; + } + + initSerial(serial: number): void { + // console.log(serial) + this.serial = serial; + } +} + +export const GraphicIdGenerator: IdGenerator = new IdGenerator(''); diff --git a/src/jlgraphic/core/JlGraphic.ts b/src/jlgraphic/core/JlGraphic.ts new file mode 100644 index 0000000..161cd9c --- /dev/null +++ b/src/jlgraphic/core/JlGraphic.ts @@ -0,0 +1,865 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-function */ +import { Viewport } from 'pixi-viewport'; +import { + Container, + DisplayObject, + Graphics, + IPointData, + Point, + Rectangle, +} from 'pixi.js'; +import { AppConsts, JlCanvas } from '../app'; +import { + convertRectangleToPolygonPoints, + recursiveChildren, + recursiveFindChild, + recursiveFindParent, + recursiveParents, +} from '../utils'; +import { GraphicRelation, RelationManage } from './GraphicRelation'; +import { GraphicQueryStore } from './GraphicStore'; +import { GraphicIdGenerator } from './IdGenerator'; + +//基础图形对象扩展 +DisplayObject.prototype._selectable = false; //是否可选中 +DisplayObject.prototype._selected = false; +DisplayObject.prototype._childEdit = false; +DisplayObject.prototype._transformSave = false; +DisplayObject.prototype._assistantAppendMap = null; +DisplayObject.prototype._draggable = false; +DisplayObject.prototype._dragStartPoint = null; +DisplayObject.prototype._scalable = false; +DisplayObject.prototype._keepAspectRatio = true; +DisplayObject.prototype._rotatable = false; +Object.defineProperties(DisplayObject.prototype, { + assistantAppendMap: { + get() { + if (this._assistantAppendMap == null) { + this._assistantAppendMap = new Map(); + } + return this._assistantAppendMap; + }, + }, + selectable: { + get(): boolean { + return this._selectable; + }, + set(value: boolean): void { + this._selectable = value; + }, + }, + selected: { + get(): boolean { + return this._selected; + }, + set(v) { + this._selected = v; + }, + }, + childEdit: { + get() { + return this._childEdit; + }, + set(v) { + this._childEdit = v; + }, + }, + transformSave: { + get() { + return this._transformSave; + }, + set(v) { + this._transformSave = v; + }, + }, + draggable: { + get(): boolean { + return this._draggable; + }, + set(v) { + this._draggable = v; + }, + }, + dragStartPoint: { + get() { + return this._dragStartPoint; + }, + set(v) { + this._dragStartPoint = v; + }, + }, + scalable: { + get() { + return this._scalable; + }, + set(v) { + this._scalable = v; + }, + }, + keepAspectRatio: { + get(): boolean { + return this._keepAspectRatio; + }, + set(v) { + this._keepAspectRatio = v; + }, + }, + rotatable: { + get() { + return this._rotatable; + }, + set(v) { + this._rotatable = v; + }, + }, + worldAngle: { + get() { + let angle = this.angle; + let parent = this.parent; + while (parent != undefined && parent != null) { + angle += parent.angle; + parent = parent.parent; + } + angle = angle % 360; + if (angle > 180) { + angle = angle - 360; + } + return angle; + }, + }, +}); +DisplayObject.prototype.getAllParentScaled = + function getAllParentScaled(): Point { + const scaled = new Point(1, 1); + recursiveParents(this, (parent) => { + scaled.x *= parent.scale.x; + scaled.y *= parent.scale.y; + }); + return scaled; + }; +DisplayObject.prototype.saveTransform = function saveTransform() { + return GraphicTransform.fromObject(this); +}; +DisplayObject.prototype.loadTransform = function loadTransform( + transfrom: GraphicTransform +) { + this.position.copyFrom(transfrom.position); + this.scale.copyFrom(transfrom.scale); + this.rotation = transfrom.rotation; + this.skew.copyFrom(transfrom.skew); +}; +DisplayObject.prototype.isChild = function isChild( + obj: DisplayObject +): boolean { + return recursiveFindChild(this as Container, (child) => child == obj) != null; +}; +DisplayObject.prototype.isParent = function isParent( + obj: DisplayObject +): boolean { + return recursiveFindParent(this, (parent) => parent == obj) != null; +}; +DisplayObject.prototype.isAssistantAppend = + function isAssistantAppend(): boolean { + return ( + recursiveFindParent(this, (parent) => { + return parent.name === AppConsts.AssistantAppendsName; + }) != null + ); + }; +DisplayObject.prototype.addAssistantAppend = function addAssistantAppend< + D extends DisplayObject +>(...appends: D[]): void { + appends.forEach((append) => { + if (append.name == null || append.name.trim() == '') { + throw new Error('辅助附加name不能为空'); + } + this.assistantAppendMap.set(append.name, append); + this.getCanvas().addAssistantAppends(append); + }); +}; +DisplayObject.prototype.getAssistantAppend = function getAssistantAppend< + D extends DisplayObject +>(name: string): D | undefined { + return this.assistantAppendMap.get(name) as D; +}; +DisplayObject.prototype.removeAssistantAppend = function removeAssistantAppend( + ...appends: DisplayObject[] +): void { + appends.forEach((append) => { + if (append.name) { + this.removeAssistantAppendByName(append.name); + } + }); +}; +DisplayObject.prototype.removeAssistantAppendByName = + function removeAssistantAppendByName(...names: string[]): void { + names.forEach((name) => { + const append = this.getAssistantAppend(name); + if (append) { + this.getCanvas().removeAssistantAppends(append); + this.assistantAppendMap.delete(name); + append.destroy(); + } + }); + }; +DisplayObject.prototype.removeAllAssistantAppend = + function removeAllAssistantAppend(): void { + if (this._assistantAppendMap != null) { + this.assistantAppendMap.forEach((append) => { + append.getCanvas().removeAssistantAppends(append); + }); + this.assistantAppendMap.clear(); + } + }; + +DisplayObject.prototype.isGraphic = function isGraphic() { + return Object.hasOwn(this, '__JlGraphic'); +}; +DisplayObject.prototype.getGraphic = function getGraphic< + G extends JlGraphic +>(): G | null { + let graphic = this as DisplayObject; + while (graphic && !Object.hasOwn(graphic, '__JlGraphic')) { + graphic = graphic.parent; + } + if (graphic) { + return graphic as G; + } + return null; +}; +DisplayObject.prototype.isGraphicChild = function isGraphicChild() { + const g = this.getGraphic(); + return g != null && !this.isAssistantAppend() && g.isChild(this); +}; +DisplayObject.prototype.onAddToCanvas = function onAddToCanvas() {}; +DisplayObject.prototype.onRemoveFromCanvas = function onRemoveFromCanvas() {}; +DisplayObject.prototype.isInCanvas = function isInCanvas(): boolean { + let graphic = this as DisplayObject; + while (graphic && !Object.hasOwn(graphic, '__JlCanvas')) { + graphic = graphic.parent; + } + if (graphic) { + return true; + } + return false; +}; +DisplayObject.prototype.getCanvas = function getCanvas() { + let graphic = this as DisplayObject; + while (graphic && !Object.hasOwn(graphic, '__JlCanvas')) { + graphic = graphic.parent; + } + if (graphic) { + return graphic as JlCanvas; + } + throw new Error(`图形${this.name}不在画布中`); +}; +DisplayObject.prototype.getViewport = function getViewport() { + const canvas = this.getCanvas(); + return canvas.parent as Viewport; +}; +DisplayObject.prototype.getGraphicApp = function getGraphicApp() { + const canvas = this.getCanvas(); + return canvas.app; +}; +DisplayObject.prototype.localToCanvasPoint = function localToCanvasPoint( + p: IPointData +): Point { + return this.getViewport().toWorld(this.toGlobal(p)); +}; +DisplayObject.prototype.localToCanvasPoints = function localToCanvasPoints( + ...points: IPointData[] +): Point[] { + return points.map((p) => this.localToCanvasPoint(p)); +}; +DisplayObject.prototype.canvasToLocalPoint = function canvasToLocalPoint( + p: IPointData +): Point { + return this.toLocal(this.getViewport().toScreen(p)); +}; +DisplayObject.prototype.canvasToLocalPoints = function canvasToLocalPoints( + ...points: IPointData[] +): Point[] { + return points.map((p) => this.canvasToLocalPoint(p)); +}; +DisplayObject.prototype.localToScreenPoint = function localToScreenPoint( + p: IPointData +): Point { + return this.toGlobal(p); +}; +DisplayObject.prototype.localToScreenPoints = function localToScreenPoints( + ...points: IPointData[] +): Point[] { + return points.map((p) => this.toGlobal(p)); +}; + +DisplayObject.prototype.localBoundsToCanvasPoints = + function localBoundsToCanvasPoints() { + const rect = this.getLocalBounds(); + const pps = convertRectangleToPolygonPoints(rect); + return this.localToCanvasPoints(...pps); + }; + +// 扩展pixijs图形对象,添加自定义绘制贝塞尔曲线,可自定义分段数 +Graphics.prototype.drawBezierCurve = function drawBezierCurve( + p1: IPointData, + p2: IPointData, + cp1: IPointData, + cp2: IPointData, + segmentsCount: number +): Graphics { + const fromX = p1.x; + const fromY = p1.y; + const n = segmentsCount; + let dt = 0; + let dt2 = 0; + let dt3 = 0; + let t2 = 0; + let t3 = 0; + const cpX = cp1.x; + const cpY = cp1.y; + const cpX2 = cp2.x; + const cpY2 = cp2.y; + const toX = p2.x; + const toY = p2.y; + this.moveTo(p1.x, p1.y); + for (let i = 1, j = 0; i <= n; ++i) { + j = i / n; + dt = 1 - j; + dt2 = dt * dt; + dt3 = dt2 * dt; + t2 = j * j; + t3 = t2 * j; + const px = dt3 * fromX + 3 * dt2 * j * cpX + 3 * dt * t2 * cpX2 + t3 * toX; + const py = dt3 * fromY + 3 * dt2 * j * cpY + 3 * dt * t2 * cpY2 + t3 * toY; + this.lineTo(px, py); + } + return this; +}; + +export interface IGraphicTransform { + position: IPointData; + scale: IPointData; + rotation: number; + skew: IPointData; +} + +/** + * 图形变换数据 + */ +export class GraphicTransform { + position: IPointData; + scale: IPointData; + rotation: number; + skew: IPointData; + + constructor( + position: IPointData, + scale: IPointData, + rotation: number, + skew: IPointData + ) { + this.position = position; + this.scale = scale; + this.rotation = rotation; + this.skew = skew; + } + static default(): GraphicTransform { + return new GraphicTransform( + new Point(0, 0), + new Point(1, 1), + 0, + new Point(0, 0) + ); + } + static fromObject(obj: DisplayObject): GraphicTransform { + return new GraphicTransform( + obj.position.clone(), + obj.scale.clone(), + obj.rotation, + obj.skew.clone() + ); + } + static from(transform: IGraphicTransform | undefined): GraphicTransform { + if (transform) { + return new GraphicTransform( + new Point(transform.position.x, transform.position.y), + new Point(transform.scale.x, transform.scale.y), + transform.rotation, + new Point(transform.skew.x, transform.skew.y) + ); + } + return GraphicTransform.default(); + } +} + +export interface IChildTransform { + name: string; + transform: IGraphicTransform; +} + +/** + * 图形子元素变换 + */ +export class ChildTransform { + name: string; + transform: GraphicTransform; + constructor(name: string, transform: GraphicTransform) { + this.name = name; + this.transform = transform; + } + static fromChild(child: DisplayObject): ChildTransform { + if ( + child.name == null || + child.name == undefined || + child.name.trim() == '' + ) { + throw new Error( + `图形type=${ + child.getGraphic()?.type + }的子元素${child}name为空,但设置为需要保存变换数据` + ); + } + return new ChildTransform(child.name, GraphicTransform.fromObject(child)); + } + static from(ct: IChildTransform): ChildTransform { + return new ChildTransform(ct.name, GraphicTransform.from(ct.transform)); + } +} + +/** + * 图形数据 + */ +export interface GraphicData { + get id(): string; // 图形id + set id(v: string); + get graphicType(): string; // 图形类型 + set graphicType(v: string); + get transform(): GraphicTransform; // + set transform(v: GraphicTransform); + get childTransforms(): ChildTransform[] | undefined; // + set childTransforms(v: ChildTransform[] | undefined); + + /** + * 克隆消息 + */ + clone(): GraphicData; + /** + * 从给定数据拷贝 + * @param data + */ + copyFrom(data: GraphicData): void; + /** + * 是否相等 + * @param data + */ + eq(data: GraphicData): boolean; +} + +/** + * 图形状态 + */ +export interface GraphicState { + get code(): string; // 业务标识 + get graphicType(): string; // 图形类型 + /** + * 克隆消息 + */ + clone(): GraphicState; + /** + * 从给定数据拷贝 + * @param data + */ + copyFrom(data: GraphicState): void; + /** + * 是否相等 + * @param data + */ + eq(data: GraphicState): boolean; +} + +/** + * 图形对象基类 + */ +export abstract class JlGraphic extends Container { + readonly __JlGraphic = true as const; + readonly type: string; // 图形类型 + private _id = ''; // 图形的唯一标识,不具有业务意义,唯一,不可重复,可用做图形数据关联。 + private _code = ''; // 业务编号/名称,用于标识图形对象,具有业务意义 + _datas?: GraphicData; // 图形数据 + _states?: GraphicState; // 图形状态数据 + private _relationManage?: RelationManage; // 图形关系管理 + private _queryStore?: GraphicQueryStore; // 图形对象查询仓库 + + constructor(type: string) { + super(); + this.type = type; + this.draggable = false; + this.filters; + } + + /** + * 更新选中状态 + * @param selected + * @returns 是否更新 + */ + updateSelected(selected: boolean): boolean { + if (this.selected !== selected) { + this.selected = selected; + this.fireSelected(); + return true; + } + return false; + } + + invertSelected() { + this.selected = !this.selected; + this.fireSelected(); + } + + fireSelected() { + if (this.selected) { + this.emit('selected', this); + } else { + this.exitChildEdit(); + this.removeAllChildSelected(); + this.emit('unselected', this); + } + } + + hasSelectedChilds(): boolean { + return ( + recursiveFindChild(this, (child) => { + if (child.selected) { + return true; + } + return false; + }) != null + ); + } + setChildSelected(child: DisplayObject): boolean { + if (child.isGraphicChild() && child.selectable) { + this.removeAllChildSelected(); + child.selected = true; + this.fireChildSelected(child); + } + return false; + } + invertChildSelected(child: DisplayObject): boolean { + if (child.isGraphicChild() && child.selectable) { + child.selected = !child.selected; + this.fireChildSelected(child); + } + return false; + } + removeAllChildSelected() { + recursiveChildren(this, (child) => { + if (child.selected) { + child.selected = false; + this.fireChildSelected(child); + } + }); + } + fireChildSelected(child: DisplayObject) { + if (child.selected) { + this.emit('childselected', child); + } else { + this.emit('childunselected', child); + } + } + exitChildEdit() { + this.childEdit = false; + this.removeAllChildSelected(); + } + + /** + * 是否此对象id/code + */ + isIdOrCode(s: string): boolean { + return this.id === s || this.code === s; + } + + /** + * 获取图形id,如果图形数据对象存在,则返回图形数据对象id + */ + public get id(): string { + if (this._datas) { + return this._datas.id; + } + return this._id; + } + /** + * 设置图形id,如果图形数据存在,则同时设置图形数据id + */ + public set id(v: string) { + this._id = v; + if (this._datas) { + this._datas.id = v; + } + } + /** + * 获取图形业务code,如果业务code在图形数据或图形状态中,则需要重写此方法 + */ + public get code(): string { + return this._code; + } + /** + * 设置图形业务code,如果业务code在图形数据或图形状态中,则需要重写此方法 + */ + public set code(v: string) { + this._code = v; + } + + getDatas(): D { + if (this._datas) { + return this._datas as D; + } + throw new Error(`id=${this.id},type=${this.type}的图形没有数据`); + } + + getStates(): S { + if (this._states) { + return this._states as S; + } + throw new Error(`id=${this.id},type=${this.type}的的图形没有状态`); + } + + public get queryStore(): GraphicQueryStore { + if (this._queryStore) { + return this._queryStore; + } + throw new Error(`type=${this.type}的图形没有QueryStore`); + } + + public set queryStore(v: GraphicQueryStore) { + this._queryStore = v; + } + + public get relationManage(): RelationManage { + if (this._relationManage) { + return this._relationManage; + } + throw new Error(`type=${this.type}的图形没有关系管理`); + } + + public set relationManage(v: RelationManage) { + this._relationManage = v; + } + + /** + * 构建图形关系 + * @param g + */ + buildRelation() {} + + /** + * 从数据加载恢复图形关系 + */ + loadRealtions() {} + + /** + * 获取当前图形的所有图形关系 + * @returns + */ + getAllRelations(): GraphicRelation[] { + return this.relationManage.getRelationsOfGraphic(this); + } + /** + * 获取当前图形的所有指定类型图形关系 + * @param type + * @returns + */ + queryRelationByType(type: string): GraphicRelation[] { + return this.relationManage.getRelationsOfGraphicAndOtherType(this, type); + } + /** + * 删除当前图形关联的指定类型的关系 + * @param type + */ + deleteRelationByType(type: string): void { + this.relationManage.deleteRelationOfGraphicAndOtherType(this, type); + } + + /** + * 构建并保存关系数据到datas中 + */ + saveRelations(): void {} + /** + * 保存数据,复制,非原始数据 + * @returns + */ + saveData(): D { + this.getDatas().graphicType = this.type; + this.getDatas().transform = GraphicTransform.fromObject(this); + this.getDatas().childTransforms = this.buildChildTransforms(); + this.saveRelations(); + return this.getDatas().clone() as D; + } + /** + * 构建子元素变换列表 + * @returns + */ + private buildChildTransforms(): ChildTransform[] { + const childTransforms: ChildTransform[] = []; + recursiveChildren(this, (child) => { + if (child.transformSave) { + childTransforms.push(ChildTransform.fromChild(child)); + } + }); + return childTransforms; + } + + /** + * 加载数据 + * @param data + */ + loadData(data: GraphicData) { + if (data.graphicType !== this.type) { + throw new Error( + `不同的图形类型,请检查数据是否正常: data.graphicType="${data.graphicType}, type="${this.type}` + ); + } + this._datas = data; + this.loadTransformFrom(data); + } + + private loadTransformFrom(data: GraphicData) { + if (data.transform) { + this.loadTransform(data.transform); + } + if (data.childTransforms) { + data.childTransforms.forEach((ct) => { + const child = this.getChildByName(ct.name, true); + if (child) { + child.loadTransform(ct.transform); + } + }); + } + } + /** + * 更新图形数据 + * @param data + * @returns + */ + updateData(data: GraphicData): boolean { + let update = false; + if (!this.getDatas().eq(data)) { + update = true; + const old = this.getDatas().clone(); + this.getDatas().copyFrom(data); + this.onDataChange(data, old); + this.loadTransformFrom(data); + this.emit('dataupdate', this, data, old); + this.repaint(); + } + return update; + } + /** + * 图形数据更新 + */ + onDataChange(newVal: GraphicData, old?: GraphicData): void {} + + /** + * 加载状态 + * @param state + */ + loadState(state: GraphicState) { + if (state.graphicType !== this.type) { + throw new Error( + `不同的图形类型,请检查数据是否正常: state.graphicType="${state.graphicType}, type="${this.type}` + ); + } + this._states = state; + } + /** + * 更新状态 + * @param state + * @returns + */ + updateStates(state: GraphicState): boolean { + let stateChange = false; + if (!this.getStates().eq(state)) { + // 判断并更新状态,默认状态 + const old = this.getStates().clone(); + this.getStates().copyFrom(state); + this.onStateChange(state, old); + stateChange = true; + this.emit('stateupdate', this, state, old); + this.repaint(); + } + return stateChange; + } + /** + * 图形状态更新处理 + */ + onStateChange(newVal: GraphicState, old?: GraphicState): void {} + + repaint(): void { + this.doRepaint(); + this.emit('repaint', this); + } + + /** + * 处理重绘逻辑 + */ + abstract doRepaint(): void; + /** + * 处理删除逻辑 + */ + onDelete(): void { + this.removeAllAssistantAppend(); + this.removeAllListeners(); + recursiveChildren(this, (child) => child.removeAllAssistantAppend()); + } + + /** + * 框选检测,默认取图形包围盒判定,若需要精细判定-子类重写此方法 + * @param box + * @returns + */ + boxIntersectCheck(box: Rectangle): boolean { + return box.intersects(this.getLocalBounds(), this.localTransform); + } +} + +/** + * 图形对象模板 + */ +export abstract class JlGraphicTemplate { + readonly type: string; + + constructor(type: string) { + this.type = type; + } + /** + * 初始化一个新的图形对象 + */ + abstract new(): G; + /** + * 加载图形对象需要用到的资源 + */ + loadAsserts(): void {} + + /** + * 克隆图形对象 + * @param graphic + * @returns + */ + clone(graphic: G): G { + const g = this.new(); + if (graphic._datas) { + // 数据克隆 + const datas = graphic.saveData(); + g.updateData(datas); + } + if (graphic._states) { + // 状态克隆 + const state = graphic.getStates().clone(); + g.updateStates(state); + } + g.id = GraphicIdGenerator.next(); + return g; + } +} + +export type GraphicTemplate = JlGraphicTemplate; diff --git a/src/jlgraphic/core/index.ts b/src/jlgraphic/core/index.ts new file mode 100644 index 0000000..742c1e9 --- /dev/null +++ b/src/jlgraphic/core/index.ts @@ -0,0 +1,4 @@ +export * from './JlGraphic'; +export * from './IdGenerator'; +export * from './GraphicRelation'; +export * from './GraphicStore'; diff --git a/src/jlgraphic/global.d.ts b/src/jlgraphic/global.d.ts new file mode 100644 index 0000000..a349bca --- /dev/null +++ b/src/jlgraphic/global.d.ts @@ -0,0 +1,113 @@ +declare namespace GlobalMixins { + type JlCanvasType = import('./app').JlCanvas; + type CanvasProperties = import('./app').ICanvasProperties; + type GraphicApp = import('./app').GraphicApp; + type JlGraphicType = import('./core').JlGraphic; + type GraphicData = import('./core').GraphicData; + type GraphicState = import('./core').GraphicState; + type GraphicTransform = import('./core').GraphicTransform; + type AppDragEventType = import('./plugins').GraphicDragEvent; + type BoundsGraphic = import('./graphic').BoundsGraphic; + type IPointDataType = import('pixi.js').IPointData; + type PointType = import('pixi.js').Point; + type DisplayObjectType = import('pixi.js').DisplayObject; + type ContainerType = import('pixi.js').Container; + interface DisplayObjectEvents { + canvassizechange: [JlCanvasType]; + 'enter-absorbable-area': [number | undefined, number | undefined]; + 'out-absorbable-area': [number | undefined, number | undefined]; + transforming: [JlCanvasType]; + dataupdate: [JlGraphicType, GraphicData, GraphicData | undefined]; + pointupdate: [obj: DisplayObjectType, points: IPointDataType[]]; + stateupdate: [JlGraphicType, GraphicState, GraphicState | undefined]; + repaint: [DisplayObjectType]; + propertiesupdate: [JlGraphicType | JlCanvasType]; + dragstart: [AppDragEventType]; + dragmove: [AppDragEventType]; + dragend: [AppDragEventType]; + scalestart: [DisplayObjectType]; + scalemove: [DisplayObjectType]; + scaleend: [DisplayObjectType]; + rotatestart: [DisplayObjectType]; + rotatemove: [DisplayObjectType]; + rotateend: [DisplayObjectType]; + transformstart: [DisplayObjectType]; + transformend: [DisplayObjectType]; + selected: [DisplayObjectType]; + unselected: [DisplayObjectType]; + childselected: [DisplayObjectType]; + childunselected: [DisplayObjectType]; + } + + // eslint-disable-next-line @typescript-eslint/no-empty-interface + interface GraphicAppEvents { + propertiesupdate: [selectedData: GraphicData | CanvasProperties | null]; + } + + interface DisplayObject { + _selectable: boolean; + _selected: boolean; + selectable: boolean; //是否可选中 + selected: boolean; // 是否选中 + _childEdit: boolean; // 子元素编辑模式 + childEdit: boolean; + _transformSave: boolean; // 变换是否保存 + transformSave: boolean; // + _assistantAppendMap: Map | null; // 辅助附加图形map + assistantAppendMap: Map; + _draggable: boolean; // 是否可拖拽 + draggable: boolean; + _dragStartPoint: PointType | null; // 拖拽起始坐标 + dragStartPoint: PointType | null; + _scalable: boolean; // 是否可缩放 + scalable: boolean; + _keepAspectRatio: boolean; // 缩放是否保持纵横比,默认保持 + keepAspectRatio: boolean; + _rotatable: boolean; // 是否可旋转 + rotatable: boolean; + worldAngle: number; // 世界角度,(-180, 180] + getAllParentScaled(): PointType; + saveTransform(): GraphicTransform; // 保存变换 + loadTransform(transform: GraphicTransform): void; // 加载变换 + isChild(obj: DisplayObject): boolean; // 是否子元素 + isParent(obj: DisplayObject): boolean; // 是否父元素 + isAssistantAppend(): boolean; // 是否辅助附加图形 + addAssistantAppend(...appends: D[]): void; + removeAssistantAppend(...appends: DisplayObjectType[]): void; + removeAssistantAppendByName(...names: string[]): void; + removeAllAssistantAppend(): void; + getAssistantAppend( + name: string + ): D | undefined; // 获取辅助附加图形对象 + isGraphic(): boolean; // 是否业务图形对象 + getGraphic(): G | null; // 获取所属的图形对象 + isGraphicChild(): boolean; // 是否图形子元素 + onAddToCanvas(): void; // 添加到画布处理 + onRemoveFromCanvas(): void; //从画布移除处理 + isInCanvas(): boolean; // 是否添加到画布中 + getCanvas(): JlCanvasType; // 获取所在画布 + getViewport(): Viewport; // 获取视口 + getGraphicApp(): GraphicApp; // 获取图形app + localToCanvasPoint(p: IPointData): PointType; // 图形本地坐标转为画布坐标 + localToCanvasPoints(...points: IPointData[]): PointType[]; // 批量转换 + canvasToLocalPoint(p: IPointData): PointType; // 画布坐标转为图形本地坐标 + canvasToLocalPoints(...points: IPointData[]): PointType[]; // 批量转换 + + localToScreenPoint(p: IPointData): PointType; // 本地坐标转为屏幕坐标 + localToScreenPoints(...points: IPointData[]): PointType[]; // 批量 + screenToLocalPoint(p: IPointData): PointType; // 屏幕坐标转为本地坐标 + screenToLocalPoints(...points: IPointData[]): PointType[]; // 批量 + + localBoundsToCanvasPoints(): PointType[]; // 本地包围框转为多边形点坐标 + } + + interface Graphics { + drawBezierCurve( + p1: IPointData, + p2: IPointData, + cp1: IPointData, + cp2: IPointData, + segmentsCount: number + ): Graphics; + } +} diff --git a/src/jlgraphic/graphic/AbsorbablePosition.ts b/src/jlgraphic/graphic/AbsorbablePosition.ts new file mode 100644 index 0000000..ec379a1 --- /dev/null +++ b/src/jlgraphic/graphic/AbsorbablePosition.ts @@ -0,0 +1,173 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { + Color, + Container, + DisplayObject, + Graphics, + IPointData, + Point, +} from 'pixi.js'; +import { + calculateFootPointFromPointToLine, + calculateIntersectionPointOfCircleAndPoint, + distance, + linePoint, +} from '../utils'; +import { VectorGraphic, VectorGraphicUtil } from './VectorGraphic'; + +/** + * 抽象可吸附位置 + */ +export interface AbsorbablePosition extends Container { + /** + * 尝试吸附图形对象 + * @param objs 图形对象列表 + * @returns 如果吸附成功,返回true,否则false + */ + tryAbsorb(...objs: DisplayObject[]): boolean; +} + +/** + * 可吸附点图形参数 + */ +export const AbsorbablePointParam = { + lineWidth: 1, + lineColor: '#000000', + fillColor: '#E77E0E', + radius: 5, // 半径 +}; + +const AbsorbablePointGraphic = new Graphics(); +// AbsorbablePointGraphic.lineStyle( +// AbsorbablePointParam.lineWidth, +// AbsorbablePointParam.lineColor +// ); +AbsorbablePointGraphic.beginFill(AbsorbablePointParam.fillColor); +AbsorbablePointGraphic.drawCircle(0, 0, AbsorbablePointParam.radius); +AbsorbablePointGraphic.endFill(); + +/** + * 可吸附点 + */ +export default class AbsorbablePoint + extends Graphics + implements AbsorbablePosition, VectorGraphic +{ + _point: Point; + absorbRange: number; + scaledListenerOn = false; + + constructor(point: IPointData, absorbRange = 10) { + super(AbsorbablePointGraphic.geometry); + this._point = new Point(point.x, point.y); + this.absorbRange = absorbRange; + this.position.copyFrom(this._point); + this.interactive; + VectorGraphicUtil.handle(this); + } + tryAbsorb(...objs: DisplayObject[]): boolean { + for (let i = 0; i < objs.length; i++) { + const obj = objs[i]; + if ( + distance(this._point.x, this._point.y, obj.position.x, obj.position.y) < + this.absorbRange + ) { + obj.position.copyFrom(this._point); + return true; + } + } + return false; + } + updateOnScaled() { + const scaled = this.getAllParentScaled(); + const scale = Math.max(scaled.x, scaled.y); + this.scale.set(1 / scale, 1 / scale); + } +} + +/** + * 可吸附线 + */ +export class AbsorbableLine extends Graphics implements AbsorbablePosition { + p1: Point; + p2: Point; + absorbRange: number; + _color = '#E77E0E'; + + constructor(p1: IPointData, p2: IPointData, absorbRange = 20) { + super(); + this.p1 = new Point(p1.x, p1.y); + this.p2 = new Point(p2.x, p2.y); + this.absorbRange = absorbRange; + this.redraw(); + } + redraw() { + this.clear(); + this.lineStyle(1, new Color(this._color)); + this.moveTo(this.p1.x, this.p1.y); + this.lineTo(this.p2.x, this.p2.y); + } + + tryAbsorb(...objs: DisplayObject[]): boolean { + for (let i = 0; i < objs.length; i++) { + const obj = objs[i]; + const p = obj.position.clone(); + if (linePoint(this.p1, this.p2, p, this.absorbRange, true)) { + const fp = calculateFootPointFromPointToLine(this.p1, this.p2, p); + obj.position.copyFrom(fp); + return true; + } + } + return false; + } +} + +/** + * 可吸附圆 + */ +export class AbsorbableCircle extends Graphics implements AbsorbablePosition { + absorbRange: number; + p0: Point; + radius: number; + _color = '#E77E0E'; + + constructor(p: IPointData, radius: number, absorbRange = 10) { + super(); + this.p0 = new Point(p.x, p.y); + this.radius = radius; + this.absorbRange = absorbRange; + this.redraw(); + } + + redraw() { + this.clear(); + this.lineStyle(1, new Color(this._color)); + this.drawCircle(this.p0.x, this.p0.y, this.radius); + } + + tryAbsorb(...objs: DisplayObject[]): boolean { + for (let i = 0; i < objs.length; i++) { + const obj = objs[i]; + const len = distance( + this.p0.x, + this.p0.y, + obj.position.x, + obj.position.y + ); + if ( + len > this.radius - this.absorbRange && + len < this.radius + this.absorbRange + ) { + // 吸附,计算直线与圆交点,更新对象坐标 + const p = calculateIntersectionPointOfCircleAndPoint( + this.p0, + this.radius, + obj.position + ); + obj.position.copyFrom(p); + return true; + } + } + return false; + } +} diff --git a/src/jlgraphic/graphic/DashedLine.ts b/src/jlgraphic/graphic/DashedLine.ts new file mode 100644 index 0000000..fbed07e --- /dev/null +++ b/src/jlgraphic/graphic/DashedLine.ts @@ -0,0 +1,102 @@ +import { Container, Graphics, IPointData, Point } from 'pixi.js'; +import { angleToAxisx } from '../utils'; + +export interface IDashedLineOptions { + /** + * 每小段长度,默认4 + */ + length?: number; + /** + * 起始间隔,默认0 + */ + startSpace?: number; + /** + * 间隔长度,默认4 + */ + space?: number; + /** + * 线宽,默认1 + */ + lineWidth?: number; + /** + * 线色,默认黑 + */ + color?: string; +} + +interface ICompleteDashedLineOptions extends IDashedLineOptions { + length: number; + startSpace: number; + space: number; + lineWidth: number; + color: string; +} + +const DefaultDashedLineOptions: ICompleteDashedLineOptions = { + length: 4, + startSpace: 0, + space: 4, + lineWidth: 1, + color: '#0000ff', +}; + +export class DashedLine extends Container { + p1: Point; + p2: Point; + _options: ICompleteDashedLineOptions; + constructor(p1: IPointData, p2: IPointData, options?: IDashedLineOptions) { + super(); + const config = Object.assign({}, DefaultDashedLineOptions, options); + this._options = config; + this.p1 = new Point(p1.x, p1.y); + this.p2 = new Point(p2.x, p2.y); + this.redraw(); + } + + setOptions(options: IDashedLineOptions) { + if (options.startSpace != undefined) { + this._options.startSpace = options.startSpace; + } + if (options.length != undefined) { + this._options.length = options.length; + } + if (options.space != undefined) { + this._options.space = options.space; + } + if (options.lineWidth != undefined) { + this._options.lineWidth = options.lineWidth; + } + if (options.color != undefined) { + this._options.color = options.color; + } + this.redraw(); + } + + redraw() { + this.removeChildren(); + const p1 = this.p1; + const p2 = this.p2; + const option = this._options; + const total = Math.pow( + Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2), + 0.5 + ); + let len = option.startSpace; + while (len < total) { + let dashedLen = option.length; + if (len + option.length > total) { + dashedLen = total - len; + } + const line = new Graphics(); + line.lineStyle(option.lineWidth, option.color); + line.moveTo(len, 0); + line.lineTo(len + dashedLen, 0); + this.addChild(line); + len = len + dashedLen + option.space; + } + + this.pivot.set(0, option.lineWidth / 2); + this.position.set(p1.x, p1.y); + this.angle = angleToAxisx(p1, p2); + } +} diff --git a/src/jlgraphic/graphic/DraggablePoint.ts b/src/jlgraphic/graphic/DraggablePoint.ts new file mode 100644 index 0000000..41e4354 --- /dev/null +++ b/src/jlgraphic/graphic/DraggablePoint.ts @@ -0,0 +1,45 @@ +import { Graphics, IPointData } from 'pixi.js'; +import { VectorGraphic, VectorGraphicUtil } from './VectorGraphic'; + +/** + * 拖拽点参数 + */ +export const DraggablePointParam = { + lineWidth: 1, + lineColor: 0x000000, + fillColor: 0xffffff, + radius: 5, // 半径 +}; + +const DraggablePointGraphic = new Graphics(); +DraggablePointGraphic.lineStyle( + DraggablePointParam.lineWidth, + DraggablePointParam.lineColor +); +DraggablePointGraphic.beginFill(DraggablePointParam.fillColor); +DraggablePointGraphic.drawCircle(0, 0, DraggablePointParam.radius); +DraggablePointGraphic.endFill(); + +/** + * 拖拽点,用于更新图形属性 + */ +export class DraggablePoint extends Graphics implements VectorGraphic { + scaledListenerOn = false; + /** + * + * @param point 画布坐标点 + */ + constructor(point: IPointData) { + super(DraggablePointGraphic.geometry); + this.position.copyFrom(point); + this.interactive = true; + this.draggable = true; + this.cursor = 'crosshair'; + VectorGraphicUtil.handle(this); + } + updateOnScaled() { + const scaled = this.getAllParentScaled(); + const scale = Math.max(scaled.x, scaled.y); + this.scale.set(1 / scale, 1 / scale); + } +} diff --git a/src/jlgraphic/graphic/GraphicTransformGraphics.ts b/src/jlgraphic/graphic/GraphicTransformGraphics.ts new file mode 100644 index 0000000..369ea21 --- /dev/null +++ b/src/jlgraphic/graphic/GraphicTransformGraphics.ts @@ -0,0 +1,475 @@ +import { + Container, + DisplayObject, + Graphics, + IDestroyOptions, + Point, + Polygon, +} from 'pixi.js'; +import { GraphicDragEvent, KeyListener } from '../plugins'; +import { + angleToAxisx, + convertRectangleToPolygonPoints, + distance, + calculateLineMidpoint, +} from '../utils'; +import { DraggablePoint } from './DraggablePoint'; + +const BoundsLineStyle = { + width: 1, + color: 0x29b6f2, + alpha: 1, +}; + +/** + * 缩放、旋转辅助 + */ +export class TransformPoints extends Container { + static Name = 'transformPoints'; + static MinLength = 40; + static LeftTopName = 'lt-scale-point'; + static TopName = 't-scale-point'; + static RightTopName = 'rt-scale-point'; + static RightName = 'r-scale-point'; + static RightBottomName = 'rb-scale-point'; + static BottomName = 'b-scale-point'; + static LeftBottomName = 'lb-scale-point'; + static LeftName = 'l-scale-point'; + + static RotateName = 'rotate-point'; + obj: DisplayObject; + + ltScalePoint: DraggablePoint; + ltLocal: Point = new Point(); + tScalePoint: DraggablePoint; + tLocal: Point = new Point(); + tCanvas: Point = new Point(); + rtScalePoint: DraggablePoint; + rtLocal: Point = new Point(); + rScalePoint: DraggablePoint; + rLocal: Point = new Point(); + rbScalePoint: DraggablePoint; + rbLocal: Point = new Point(); + bScalePoint: DraggablePoint; + bLocal: Point = new Point(); + lbScalePoint: DraggablePoint; + lbLocal: Point = new Point(); + lScalePoint: DraggablePoint; + lLocal: Point = new Point(); + originScale: Point = new Point(); + scalePivot: Point = new Point(); + + /** + * 旋转拖拽点 + */ + rotatePoint: DraggablePoint; + /** + * 旋转中心坐标 + */ + rotatePivot: Point; + /** + * 起始旋转坐标 + */ + rotateLastPoint: Point; + /** + * 起始图形角度 + */ + startAngle = 0; + /** + * 旋转角度步长 + */ + angleStep = 1; + /** + * 修改旋转步长键盘监听 + */ + rotateAngleStepKeyListeners: KeyListener[] = []; + + constructor(obj: DisplayObject) { + super(); + this.obj = obj; + this.name = TransformPoints.Name; + + // 创建缩放拖拽点 + this.ltScalePoint = new DraggablePoint(new Point()); + this.ltScalePoint.name = TransformPoints.LeftTopName; + this.addChild(this.ltScalePoint); + this.tScalePoint = new DraggablePoint(new Point()); + this.tScalePoint.name = TransformPoints.TopName; + this.addChild(this.tScalePoint); + this.rtScalePoint = new DraggablePoint(new Point()); + this.rtScalePoint.name = TransformPoints.RightTopName; + this.addChild(this.rtScalePoint); + this.rScalePoint = new DraggablePoint(new Point()); + this.rScalePoint.name = TransformPoints.RightName; + this.addChild(this.rScalePoint); + this.rbScalePoint = new DraggablePoint(new Point()); + this.rbScalePoint.name = TransformPoints.RightBottomName; + this.addChild(this.rbScalePoint); + this.bScalePoint = new DraggablePoint(new Point()); + this.bScalePoint.name = TransformPoints.BottomName; + this.addChild(this.bScalePoint); + this.lbScalePoint = new DraggablePoint(new Point()); + this.lbScalePoint.name = TransformPoints.LeftBottomName; + this.addChild(this.lbScalePoint); + this.lScalePoint = new DraggablePoint(new Point()); + this.lScalePoint.name = TransformPoints.LeftName; + this.addChild(this.lScalePoint); + this.obj.on('transformstart', this.onObjTransformStart, this); + this.obj.on('transformend', this.onObjTransformEnd, this); + this.obj.on('repaint', this.onGraphicRepaint, this); + this.children.forEach((dp) => { + dp.on('dragstart', this.onScaleDragStart, this); + dp.on('dragmove', this.onScaleDragMove, this); + dp.on('dragend', this.onScaleDragEnd, this); + }); + + // 创建旋转拖拽点 + this.rotatePoint = new DraggablePoint(new Point()); + this.addChild(this.rotatePoint); + this.rotatePoint.on('dragstart', this.onRotateStart, this); + this.rotatePoint.on('dragmove', this.onRotateMove, this); + this.rotatePoint.on('dragend', this.onRotateEnd, this); + this.rotatePivot = new Point(); + this.rotateLastPoint = new Point(); + // 初始化旋转角度修改键盘监听器 + for (let i = 1; i < 10; i++) { + this.rotateAngleStepKeyListeners.push( + KeyListener.create({ + value: '' + i, + onPress: () => { + // console.log('修改角度step'); + this.angleStep = i; + }, + }) + ); + } + + this.obj.addAssistantAppend(this); + } + + onObjTransformStart() { + this.visible = false; + } + onObjTransformEnd() { + this.update(); + this.visible = true; + } + onGraphicRepaint() { + if (this.visible) { + this.update(); + } + } + + /** + * 旋转开始 + * @param de + */ + onRotateStart(de: GraphicDragEvent) { + this.hideAll(); + const assistantPoint = this.obj.localToCanvasPoint(this.obj.pivot); + this.rotatePivot.copyFrom(assistantPoint); + this.rotateLastPoint.copyFrom(de.target.position); + this.startAngle = this.obj.angle; + this.rotateAngleStepKeyListeners.forEach((listener) => + this.obj.getGraphicApp().addKeyboardListener(listener) + ); + this.obj.emit('rotatestart', this.obj); + this.obj.emit('transformstart', this.obj); + } + /** + * 旋转移动 + * @param de + */ + onRotateMove(de: GraphicDragEvent) { + // 旋转角度计算逻辑:取锚点y负方向一点作为旋转点,求旋转点和锚点所形成的直线与x轴角度,此角度+90°即为最终旋转角度,再将旋转角度限制到(-180,180]之间 + let angle = angleToAxisx(this.rotatePivot, de.target.position); + angle = Math.floor(angle / this.angleStep) * this.angleStep; + angle = (angle + 90) % 360; + if (angle > 180) { + angle = angle - 360; + } + this.obj.angle = angle; + this.obj.emit('rotatemove', this.obj); + } + /** + * 旋转结束 + * @param de + */ + onRotateEnd() { + this.showAll(); + this.rotateAngleStepKeyListeners.forEach((listener) => + this.obj.getGraphicApp().removeKeyboardListener(listener) + ); + this.obj.emit('rotateend', this.obj); + this.obj.emit('transformend', this.obj); + } + + /** + * 缩放开始 + */ + onScaleDragStart() { + this.hideAll(); + const points = convertRectangleToPolygonPoints(this.obj.getLocalBounds()); + const p0 = points[0]; + const p1 = points[1]; + const p2 = points[2]; + const p3 = points[3]; + this.scalePivot = this.obj.pivot.clone(); + this.ltLocal.copyFrom(p0); + this.tCanvas.copyFrom( + this.obj.localToCanvasPoint(calculateLineMidpoint(p0, p1)) + ); + this.tLocal.copyFrom(calculateLineMidpoint(p0, p1)); + this.rtLocal.copyFrom(p1); + this.rLocal.copyFrom(calculateLineMidpoint(p1, p2)); + this.rbLocal.copyFrom(p2); + this.bLocal.copyFrom(calculateLineMidpoint(p2, p3)); + this.lbLocal.copyFrom(p3); + this.lLocal.copyFrom(calculateLineMidpoint(p0, p3)); + this.originScale = this.obj.scale.clone(); + this.obj.emit('scalestart', this.obj); + this.obj.emit('transformstart', this.obj); + } + + onScaleDragMove(de: GraphicDragEvent) { + // 缩放计算逻辑:共8个方向缩放点,根据所拖拽的方向: + // 1,计算缩放为1时的此点在拖拽开始时的位置到锚点x、y距离, + // 2,计算拖拽点的当前位置到锚点的x、y方向距离, + // PS:以上两个计算都是在local(也就是图形对象本地)坐标系, + // 用当前距离除以原始距离即为缩放比例 + const defaultScale = new Point(1, 1); + let originWidth = 0; + let originHeight = 0; + let width = 0; + let height = 0; + this.obj.scale.copyFrom(defaultScale); + const point = this.obj.toLocal(de.event.global); + if (de.target === this.ltScalePoint) { + // 左上角 + originWidth = Math.abs(this.ltLocal.x - this.scalePivot.x); + originHeight = Math.abs(this.ltLocal.y - this.scalePivot.y); + width = Math.abs(point.x - this.scalePivot.x); + height = Math.abs(point.y - this.scalePivot.y); + } else if (de.target == this.tScalePoint) { + // 上 + originHeight = Math.abs(this.tLocal.y - this.scalePivot.y); + height = Math.abs(point.y - this.scalePivot.y); + } else if (de.target == this.rtScalePoint) { + // 右上 + originWidth = Math.abs(this.rtLocal.x - this.scalePivot.x); + originHeight = Math.abs(this.rtLocal.y - this.scalePivot.y); + width = Math.abs(point.x - this.scalePivot.x); + height = Math.abs(point.y - this.scalePivot.y); + } else if (de.target == this.rScalePoint) { + // 右 + originWidth = Math.abs(this.rLocal.x - this.scalePivot.x); + width = Math.abs(point.x - this.scalePivot.x); + } else if (de.target == this.rbScalePoint) { + // 右下 + originWidth = Math.abs(this.rbLocal.x - this.scalePivot.x); + originHeight = Math.abs(this.rbLocal.y - this.scalePivot.y); + width = Math.abs(point.x - this.scalePivot.x); + height = Math.abs(point.y - this.scalePivot.y); + } else if (de.target == this.bScalePoint) { + // 下 + originHeight = Math.abs(this.bLocal.y - this.scalePivot.y); + height = Math.abs(point.y - this.scalePivot.y); + } else if (de.target == this.lbScalePoint) { + // 左下 + originWidth = Math.abs(this.lbLocal.x - this.scalePivot.x); + originHeight = Math.abs(this.lbLocal.y - this.scalePivot.y); + width = Math.abs(point.x - this.scalePivot.x); + height = Math.abs(point.y - this.scalePivot.y); + } else { + // 左 + originWidth = Math.abs(this.lLocal.x - this.scalePivot.x); + width = Math.abs(point.x - this.scalePivot.x); + } + // 计算缩放比例,并根据是否保持纵横比两种情况进行缩放处理 + const sx = originWidth == 0 ? this.originScale.x : width / originWidth; + const sy = originHeight == 0 ? this.originScale.y : height / originHeight; + // console.log(originWidth, originHeight, width, height, sx, sy); + if (this.obj.keepAspectRatio) { + let max = Math.max(sx, sy); + if (originWidth == 0) { + max = sy; + } else if (originHeight == 0) { + max = sx; + } + this.obj.scale.set(max, max); + } else { + this.obj.scale.x = sx; + this.obj.scale.y = sy; + } + } + + onScaleDragEnd() { + this.showAll(); + this.obj.emit('scaleend', this.obj); + this.obj.emit('transformend', this.obj); + this.obj.getGraphicApp().emit('scaleend', this.obj); + } + + hideOthers(dg: DisplayObject) { + this.children.forEach((child) => { + if (child.name !== dg.name) { + child.visible = false; + } + }); + } + + hideAll() { + this.children.forEach((child) => (child.visible = false)); + } + + showAll() { + this.update(); + this.children.forEach((child) => (child.visible = true)); + } + + getObjBounds(): { width: number; height: number } { + const points = this.obj.localBoundsToCanvasPoints(); + const p0 = points[0]; + const p1 = points[1]; + const p3 = points[3]; + const width = distance(p0.x, p0.y, p1.x, p1.y); + const height = distance(p0.x, p0.y, p3.x, p3.y); + return { width, height }; + } + + /** + * 更新位置和cursor + * @returns + */ + update() { + if (this.obj.scalable) { + this.updateScalePoints(); + } + if (this.obj.rotatable) { + this.updateRotatePoint(); + } + } + + updateRotatePoint() { + const rect = this.obj.getLocalBounds(); + const lp = this.obj.pivot.clone(); + const dy = 10 / this.obj.scale.y; + lp.y = rect.y - dy; + const p = this.obj.localToCanvasPoint(lp); + this.rotatePoint.position.copyFrom(p); + } + + updateScalePoints() { + const points = this.obj.localBoundsToCanvasPoints(); + this.ltScalePoint.position.copyFrom(points[0]); + this.tScalePoint.position.copyFrom( + calculateLineMidpoint(points[0], points[1]) + ); + this.rtScalePoint.position.copyFrom(points[1]); + this.rScalePoint.position.copyFrom( + calculateLineMidpoint(points[1], points[2]) + ); + this.rbScalePoint.position.copyFrom(points[2]); + this.bScalePoint.position.copyFrom( + calculateLineMidpoint(points[2], points[3]) + ); + this.lbScalePoint.position.copyFrom(points[3]); + this.lScalePoint.position.copyFrom( + calculateLineMidpoint(points[3], points[0]) + ); + const angle = this.obj.worldAngle; + const angle360 = (360 + angle) % 360; + if ( + (angle >= -22.5 && angle <= 22.5) || + (angle360 >= 180 - 22.5 && angle360 <= 180 + 22.5) + ) { + this.ltScalePoint.cursor = 'nw-resize'; + this.tScalePoint.cursor = 'n-resize'; + this.rtScalePoint.cursor = 'ne-resize'; + this.rScalePoint.cursor = 'e-resize'; + this.rbScalePoint.cursor = 'se-resize'; + this.bScalePoint.cursor = 's-resize'; + this.lbScalePoint.cursor = 'sw-resize'; + this.lScalePoint.cursor = 'w-resize'; + } else if ( + (angle >= 22.5 && angle <= 67.5) || + (angle360 >= 180 + 22.5 && angle360 <= 180 + 67.5) + ) { + this.ltScalePoint.cursor = 'n-resize'; + this.tScalePoint.cursor = 'ne-resize'; + this.rtScalePoint.cursor = 'e-resize'; + this.rScalePoint.cursor = 'se-resize'; + this.rbScalePoint.cursor = 's-resize'; + this.bScalePoint.cursor = 'sw-resize'; + this.lbScalePoint.cursor = 'w-resize'; + this.lScalePoint.cursor = 'nw-resize'; + } else if ( + (angle >= 67.5 && angle < 112.5) || + (angle360 >= 180 + 67.5 && angle360 <= 180 + 112.5) + ) { + this.ltScalePoint.cursor = 'ne-resize'; + this.tScalePoint.cursor = 'e-resize'; + this.rtScalePoint.cursor = 'se-resize'; + this.rScalePoint.cursor = 's-resize'; + this.rbScalePoint.cursor = 'sw-resize'; + this.bScalePoint.cursor = 'w-resize'; + this.lbScalePoint.cursor = 'nw-resize'; + this.lScalePoint.cursor = 'n-resize'; + } else { + this.ltScalePoint.cursor = 'e-resize'; + this.tScalePoint.cursor = 'se-resize'; + this.rtScalePoint.cursor = 's-resize'; + this.rScalePoint.cursor = 'sw-resize'; + this.rbScalePoint.cursor = 'w-resize'; + this.bScalePoint.cursor = 'nw-resize'; + this.lbScalePoint.cursor = 'n-resize'; + this.lScalePoint.cursor = 'ne-resize'; + } + } +} + +/** + * 包围盒矩形图形,现使用外边框转画布多边形实现 + */ +export class BoundsGraphic extends Graphics { + static Name = '_BoundsRect'; + obj: DisplayObject; + constructor(graphic: DisplayObject) { + super(); + this.obj = graphic; + this.name = BoundsGraphic.Name; + this.visible = false; + this.obj.on('transformstart', this.onObjTransformStart, this); + this.obj.on('transformend', this.onObjTransformEnd, this); + this.obj.on('repaint', this.onGraphicRepaint, this); + graphic.addAssistantAppend(this); + } + + onObjTransformStart(): void { + this.visible = false; + } + onObjTransformEnd(): void { + this.redraw(); + this.visible = true; + } + + onGraphicRepaint(): void { + if (this.visible) { + this.redraw(); + this.visible = true; + } + } + + destroy(options?: boolean | IDestroyOptions | undefined): void { + if (this.obj.isGraphic()) { + this.obj.off('repaint', this.onGraphicRepaint, this); + } + super.destroy(options); + } + + redraw() { + this.visible = false; // 屏蔽包围框本身 + const bounds = new Polygon(this.obj.localBoundsToCanvasPoints()); + this.clear().lineStyle(BoundsLineStyle).drawShape(bounds); + } +} diff --git a/src/jlgraphic/graphic/VectorGraphic.ts b/src/jlgraphic/graphic/VectorGraphic.ts new file mode 100644 index 0000000..8d498d3 --- /dev/null +++ b/src/jlgraphic/graphic/VectorGraphic.ts @@ -0,0 +1,44 @@ +import { DisplayObject } from 'pixi.js'; + +export interface VectorGraphic extends DisplayObject { + scaledListenerOn: boolean; + updateOnScaled(): void; +} + +export class VectorGraphicUtil { + static handle(obj: VectorGraphic): void { + const vg = obj; + const onScaleChange = function (obj: DisplayObject) { + if (vg.isParent(obj)) { + vg.updateOnScaled(); + } + }; + const registerScaleChange = function registerScaleChange( + obj: VectorGraphic + ): void { + if (!obj.scaledListenerOn) { + obj.scaledListenerOn = true; + obj.getGraphicApp().on('scaleend', onScaleChange); + } + }; + const unregisterScaleChange = function unregisterScaleChange( + obj: VectorGraphic + ): void { + obj.scaledListenerOn = false; + obj.getGraphicApp().off('scaleend', onScaleChange); + }; + obj.onAddToCanvas = function onAddToCanvas() { + obj.updateOnScaled(); + registerScaleChange(obj); + }; + obj.onRemoveFromCanvas = function onRemoveFromCanvas() { + unregisterScaleChange(obj); + }; + + obj.on('added', (container) => { + if (container.isInCanvas()) { + obj.onAddToCanvas(); + } + }); + } +} diff --git a/src/jlgraphic/graphic/VectorText.ts b/src/jlgraphic/graphic/VectorText.ts new file mode 100644 index 0000000..f28e031 --- /dev/null +++ b/src/jlgraphic/graphic/VectorText.ts @@ -0,0 +1,35 @@ +import { ICanvas, ITextStyle, Text, TextStyle } from 'pixi.js'; +import { VectorGraphic, VectorGraphicUtil } from '.'; + +/** + * 矢量文字.实现原理:在缩放发生变化时,更新fontSize + */ +export class VectorText extends Text implements VectorGraphic { + vectorFontSize = 8; + scaled = 1; + scaledListenerOn = false; + + constructor( + text?: string | number, + style?: Partial | TextStyle, + canvas?: ICanvas + ) { + super(text, style, canvas); + VectorGraphicUtil.handle(this); + } + + updateOnScaled() { + const scaled = this.getAllParentScaled(); + const scale = Math.max(scaled.x, scaled.y); + this.style.fontSize = this.vectorFontSize * scale; + this.scale.set(1 / scale, 1 / scale); + } + + /** + * 设置矢量文字的字体大小 + */ + setVectorFontSize(fontSize: number) { + this.vectorFontSize = fontSize; + this.style.fontSize = fontSize; + } +} diff --git a/src/jlgraphic/graphic/index.ts b/src/jlgraphic/graphic/index.ts new file mode 100644 index 0000000..bfce607 --- /dev/null +++ b/src/jlgraphic/graphic/index.ts @@ -0,0 +1,5 @@ +export * from './VectorGraphic'; +export * from './VectorText'; +export * from './DraggablePoint'; +export * from './AbsorbablePosition'; +export * from './GraphicTransformGraphics'; diff --git a/src/jlgraphic/index.ts b/src/jlgraphic/index.ts new file mode 100644 index 0000000..e64f743 --- /dev/null +++ b/src/jlgraphic/index.ts @@ -0,0 +1,7 @@ +export * from './core'; +export * from './graphic'; +export * from './app'; +export * from './operation'; +export * from './utils'; +export * from './plugins'; +export * from './message'; diff --git a/src/jlgraphic/math/Constants.ts b/src/jlgraphic/math/Constants.ts new file mode 100644 index 0000000..87393fb --- /dev/null +++ b/src/jlgraphic/math/Constants.ts @@ -0,0 +1,26 @@ +/** + * 浮点数相等判断误差值 + */ +export const epsilon = 0.00001; + +/** + * 判断浮点数是不是0 + * @param v + * @returns + */ +export function isZero(v: number) { + if (Math.abs(v) < epsilon) { + return true; + } + return false; +} + +/** + * 两浮点数是否相等 + * @param f1 + * @param f2 + * @returns + */ +export function floatEquals(f1: number, f2: number): boolean { + return isZero(f1 - f2); +} diff --git a/src/jlgraphic/math/Vector2.ts b/src/jlgraphic/math/Vector2.ts new file mode 100644 index 0000000..1894d6f --- /dev/null +++ b/src/jlgraphic/math/Vector2.ts @@ -0,0 +1,360 @@ +/* eslint-disable @typescript-eslint/no-this-alias */ + +import { epsilon } from './Constants'; + +export default class Vector2 { + constructor(values?: [number, number]) { + if (values !== undefined) { + this.xy = values; + } + } + + static from(p: { x: number; y: number }): Vector2 { + return new Vector2([p.x, p.y]); + } + + private values = new Float32Array(2); + + static readonly zero = new Vector2([0, 0]); + static readonly one = new Vector2([1, 1]); + + get x(): number { + return this.values[0]; + } + + set x(value: number) { + this.values[0] = value; + } + get y(): number { + return this.values[1]; + } + set y(value: number) { + this.values[1] = value; + } + + get xy(): [number, number] { + return [this.values[0], this.values[1]]; + } + + set xy(values: [number, number]) { + this.values[0] = values[0]; + this.values[1] = values[1]; + } + + at(index: number): number { + return this.values[index]; + } + + reset(): void { + this.x = 0; + this.y = 0; + } + + copy(dest?: Vector2): Vector2 { + if (!dest) { + dest = new Vector2(); + } + + dest.x = this.x; + dest.y = this.y; + + return dest; + } + + negate(dest?: Vector2): Vector2 { + if (!dest) { + dest = this; + } + + dest.x = -this.x; + dest.y = -this.y; + + return dest; + } + + equals(vector: Vector2, threshold = epsilon): boolean { + if (Math.abs(this.x - vector.x) > threshold) { + return false; + } + + if (Math.abs(this.y - vector.y) > threshold) { + return false; + } + + return true; + } + + length(): number { + return Math.sqrt(this.squaredLength()); + } + + squaredLength(): number { + const x = this.x; + const y = this.y; + + return x * x + y * y; + } + + add(vector: Vector2): Vector2 { + this.x += vector.x; + this.y += vector.y; + + return this; + } + + subtract(vector: Vector2): Vector2 { + this.x -= vector.x; + this.y -= vector.y; + + return this; + } + + multiply(vector: Vector2): Vector2 { + this.x *= vector.x; + this.y *= vector.y; + + return this; + } + + divide(vector: Vector2): Vector2 { + this.x /= vector.x; + this.y /= vector.y; + + return this; + } + + scale(value: number, dest?: Vector2): Vector2 { + if (!dest) { + dest = this; + } + + dest.x *= value; + dest.y *= value; + + return dest; + } + + normalize(dest?: Vector2): Vector2 { + if (!dest) { + dest = this; + } + + let length = this.length(); + + if (length === 1) { + return this; + } + + if (length === 0) { + dest.x = 0; + dest.y = 0; + + return dest; + } + + length = 1.0 / length; + + dest.x *= length; + dest.y *= length; + + return dest; + } + + // multiplyMat2(matrix: mat2, dest?: Vector2): Vector2 { + // if (!dest) { + // dest = this; + // } + + // return matrix.multiplyVec2(this, dest); + // } + + // multiplyMat3(matrix: mat3, dest?: Vector2): Vector2 { + // if (!dest) { + // dest = this; + // } + + // return matrix.multiplyVec2(this, dest); + // } + + // static cross(vector: Vector2, vector2: Vector2, dest?: vec3): vec3 { + // if (!dest) { + // dest = new vec3(); + // } + + // const x = vector.x; + // const y = vector.y; + + // const x2 = vector2.x; + // const y2 = vector2.y; + + // const z = x * y2 - y * x2; + + // dest.x = 0; + // dest.y = 0; + // dest.z = z; + + // return dest; + // } + + /** + * 向量点乘 + * @param vector + * @param vector2 + * @returns + */ + static dot(vector: Vector2, vector2: Vector2): number { + return vector.x * vector2.x + vector.y * vector2.y; + } + /** + * 向量长度 + * @param vector + * @param vector2 + * @returns + */ + static distance(vector: Vector2, vector2: Vector2): number { + return Math.sqrt(this.squaredDistance(vector, vector2)); + } + + /** + * 向量长度平方 + * @param vector + * @param vector2 + * @returns + */ + static squaredDistance(vector: Vector2, vector2: Vector2): number { + const x = vector2.x - vector.x; + const y = vector2.y - vector.y; + + return x * x + y * y; + } + + /** + * v2->v1的方向的单位向量 + * @param v1 + * @param v2 + * @param dest + * @returns + */ + static direction(v1: Vector2, v2: Vector2, dest?: Vector2): Vector2 { + if (!dest) { + dest = new Vector2(); + } + + const x = v1.x - v2.x; + const y = v1.y - v2.y; + + let length = Math.sqrt(x * x + y * y); + + if (length === 0) { + dest.x = 0; + dest.y = 0; + + return dest; + } + + length = 1 / length; + + dest.x = x * length; + dest.y = y * length; + + return dest; + } + + static mix( + vector: Vector2, + vector2: Vector2, + time: number, + dest?: Vector2 + ): Vector2 { + if (!dest) { + dest = new Vector2(); + } + + const x = vector.x; + const y = vector.y; + + const x2 = vector2.x; + const y2 = vector2.y; + + dest.x = x + time * (x2 - x); + dest.y = y + time * (y2 - y); + + return dest; + } + + /** + * 向量加法 + * @param vector + * @param vector2 + * @param dest + * @returns + */ + static sum(vector: Vector2, vector2: Vector2, dest?: Vector2): Vector2 { + if (!dest) { + dest = new Vector2(); + } + + dest.x = vector.x + vector2.x; + dest.y = vector.y + vector2.y; + + return dest; + } + + /** + * 向量减法 + * @param vector + * @param vector2 + * @param dest + * @returns + */ + static difference( + vector: Vector2, + vector2: Vector2, + dest?: Vector2 + ): Vector2 { + if (!dest) { + dest = new Vector2(); + } + + dest.x = vector.x - vector2.x; + dest.y = vector.y - vector2.y; + + return dest; + } + + /** + * 向量乘法 + * @param vector + * @param vector2 + * @param dest + * @returns + */ + static product(vector: Vector2, vector2: Vector2, dest?: Vector2): Vector2 { + if (!dest) { + dest = new Vector2(); + } + + dest.x = vector.x * vector2.x; + dest.y = vector.y * vector2.y; + + return dest; + } + + /** + * 向量除法 + * @param vector + * @param vector2 + * @param dest + * @returns + */ + static quotient(vector: Vector2, vector2: Vector2, dest?: Vector2): Vector2 { + if (!dest) { + dest = new Vector2(); + } + + dest.x = vector.x / vector2.x; + dest.y = vector.y / vector2.y; + + return dest; + } +} diff --git a/src/jlgraphic/math/index.ts b/src/jlgraphic/math/index.ts new file mode 100644 index 0000000..3935efe --- /dev/null +++ b/src/jlgraphic/math/index.ts @@ -0,0 +1,4 @@ +/// 向量和矩阵代码源自开源代码:https://github.com/matthiasferch/tsm + +export * from './Constants'; +export * from './Vector2'; diff --git a/src/jlgraphic/message/WsMsgBroker.ts b/src/jlgraphic/message/WsMsgBroker.ts new file mode 100644 index 0000000..dc172f8 --- /dev/null +++ b/src/jlgraphic/message/WsMsgBroker.ts @@ -0,0 +1,215 @@ +import { + Client as StompClient, + StompSubscription, + type Frame, + type Message, + type messageCallbackType, +} from '@stomp/stompjs'; +import type { GraphicApp } from '../app/JlGraphicApp'; +import { GraphicState } from '../core/JlGraphic'; + +export interface StompCliOption { + wsUrl: string; // websocket url + token: string; // 认证token + reconnectDelay?: number; // 重连延时,默认3秒 + heartbeatIncoming?: number; // 服务端过来的心跳间隔,默认30秒 + heartbeatOutgoing?: number; // 到服务端的心跳间隔,默认30秒 +} + +const DefaultStompOption: StompCliOption = { + wsUrl: '', + token: '', + reconnectDelay: 3000, + heartbeatIncoming: 30000, + heartbeatOutgoing: 30000, +}; + +export class StompCli { + private static enabled = false; + private static options: StompCliOption; + private static client: StompClient; + private static appMsgBroker: AppWsMsgBroker[] = []; + private static connected = false; + static new(options: StompCliOption) { + if (StompCli.enabled) { + // 以及启用 + return; + // throw new Error('websocket 已连接,若确实需要重新连接,请先断开StompCli.close再重新StompCli.new') + } + StompCli.enabled = true; + StompCli.options = Object.assign({}, DefaultStompOption, options); + StompCli.client = new StompClient({ + brokerURL: StompCli.options.wsUrl, + connectHeaders: { + Authorization: StompCli.options.token, + // Authorization: '' + }, + // debug: (str) => { + // console.log(str) + // } + reconnectDelay: StompCli.options.reconnectDelay, + heartbeatIncoming: StompCli.options.heartbeatIncoming, + heartbeatOutgoing: StompCli.options.heartbeatOutgoing, + }); + + StompCli.client.onConnect = () => { + // console.log('websocket连接(重连),重新订阅', StompCli.appMsgBroker.length) + StompCli.connected = true; + StompCli.appMsgBroker.forEach((broker) => { + broker.resubscribe(); + }); + }; + + StompCli.client.onStompError = (frame: Frame) => { + console.error( + 'Stomp收到error消息,可能是认证失败(暂时没有判断具体错误类型,后需添加判断),关闭Stomp客户端', + frame + ); + StompCli.close(); + }; + + StompCli.client.onDisconnect = (frame: Frame) => { + console.log('Stomp 断开连接', frame); + StompCli.connected = false; + // StompCli.appMsgBroker.forEach(broker => { + // broker.close(); + // }); + }; + StompCli.client.onWebSocketClose = (evt: CloseEvent) => { + console.log('websocket 关闭', evt); + StompCli.connected = false; + }; + // websocket错误处理 + StompCli.client.onWebSocketError = (err: Event) => { + console.log('websocket错误', err); + // StompCli.appMsgBroker.forEach(broker => { + // broker.unsbuscribeAll(); + // }); + }; + + StompCli.client.activate(); + } + + static isEnabled(): boolean { + return StompCli.enabled; + } + + static isConnected(): boolean { + return StompCli.client && StompCli.client.connected; + } + + static trySubscribe( + destination: string, + handler: messageCallbackType + ): StompSubscription | undefined { + if (StompCli.isConnected()) { + return StompCli.client.subscribe(destination, handler, { + id: destination, + }); + } + return undefined; + } + + static registerAppMsgBroker(broker: AppWsMsgBroker) { + StompCli.appMsgBroker.push(broker); + } + + static removeAppMsgBroker(broker: AppWsMsgBroker) { + const index = StompCli.appMsgBroker.findIndex((mb) => mb == broker); + if (index >= 0) { + StompCli.appMsgBroker.splice(index, 1); + } + } + + static hasAppMsgBroker(): boolean { + return StompCli.appMsgBroker.length > 0; + } + + /** + * 关闭websocket连接 + */ + static close() { + StompCli.enabled = false; + StompCli.connected = false; + if (StompCli.client) { + StompCli.client.deactivate(); + } + } +} + +// 状态订阅消息转换器 +export type MessageConverter = (message: Uint8Array) => GraphicState[]; +// 图形app状态订阅 +export class AppStateSubscription { + destination: string; + messageConverter: MessageConverter; + subscription?: StompSubscription; // 订阅成功对象,用于取消订阅 + constructor( + destination: string, + messageConverter: MessageConverter, + subscription?: StompSubscription + ) { + this.destination = destination; + this.messageConverter = messageConverter; + this.subscription = subscription; + } +} + +/** + * 图形APP的websocket消息代理 + */ +export class AppWsMsgBroker { + app: GraphicApp; + subscriptions: Map = new Map< + string, + AppStateSubscription + >(); + + constructor(app: GraphicApp) { + this.app = app; + StompCli.registerAppMsgBroker(this); + } + + subscribe(sub: AppStateSubscription) { + this.unsbuscribe(sub.destination); // 先尝试取消之前订阅 + sub.subscription = StompCli.trySubscribe( + sub.destination, + (message: Message) => { + const graphicStates = sub.messageConverter(message.binaryBody); + this.app.handleGraphicStates(graphicStates); + } + ); + // console.log('代理订阅结果', sub.subscription) + this.subscriptions.set(sub.destination, sub); + } + + resubscribe() { + this.subscriptions.forEach((record) => { + this.subscribe(record); + }); + } + + unsbuscribe(destination: string) { + const oldSub = this.subscriptions.get(destination); + if (oldSub) { + if (oldSub.subscription && StompCli.isConnected()) { + oldSub.subscription.unsubscribe(); + } + oldSub.subscription = undefined; + } + } + + unsbuscribeAll() { + this.subscriptions.forEach((record) => { + this.unsbuscribe(record.destination); + }); + } + + /** + * 取消所有订阅,从通用Stomp客户端移除此消息代理 + */ + close() { + StompCli.removeAppMsgBroker(this); + this.unsbuscribeAll(); + } +} diff --git a/src/jlgraphic/message/index.ts b/src/jlgraphic/message/index.ts new file mode 100644 index 0000000..fddbf3f --- /dev/null +++ b/src/jlgraphic/message/index.ts @@ -0,0 +1 @@ +export * from './WsMsgBroker'; diff --git a/src/jlgraphic/operation/JlOperation.ts b/src/jlgraphic/operation/JlOperation.ts new file mode 100644 index 0000000..979ad6a --- /dev/null +++ b/src/jlgraphic/operation/JlOperation.ts @@ -0,0 +1,91 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { GraphicApp } from '../app/JlGraphicApp'; +import { JlGraphic } from '../core/JlGraphic'; + +/** + * 操作 + */ +export abstract class JlOperation { + type: string; // 操作类型/名称 + app: GraphicApp; + obj?: any; // 操作对象 + data?: any; // 操作数据 + description?: string = ''; // 操作描述 + + constructor(app: GraphicApp, type: string) { + this.app = app; + this.type = type; + } + + undo1(): void { + const updates = this.undo(); + if (updates) { + this.app.updateSelected(...updates); + } + } + redo1(): void { + const updates = this.redo(); + if (updates) { + this.app.updateSelected(...updates); + } + } + + abstract undo(): JlGraphic[] | void; + abstract redo(): JlGraphic[] | void; +} + +/** + * 操作记录 + */ +export class OperationRecord { + undoStack: JlOperation[] = []; + redoStack: JlOperation[] = []; + private maxLen: number; + + constructor(maxLen = 100) { + this.maxLen = maxLen; + } + + setMaxLen(v: number) { + this.maxLen = v; + const len = this.undoStack.length; + if (len > v) { + const removeCount = len - v; + this.undoStack.splice(0, removeCount); + } + } + /** + * 记录 + * @param op + */ + record(op: JlOperation) { + if (this.undoStack.length >= this.maxLen) { + this.undoStack.shift(); + } + // console.log('operation record', op) + this.undoStack.push(op); + this.redoStack.splice(0, this.redoStack.length); + } + /** + * 撤销 + */ + undo() { + const op = this.undoStack.pop(); + // console.log('撤销', op); + if (op) { + op.undo1(); + this.redoStack.push(op); + } + } + /** + * 重做 + */ + redo() { + const op = this.redoStack.pop(); + // console.log('重做', op); + if (op) { + op.redo1(); + this.undoStack.push(op); + } + } +} diff --git a/src/jlgraphic/operation/index.ts b/src/jlgraphic/operation/index.ts new file mode 100644 index 0000000..0d6da94 --- /dev/null +++ b/src/jlgraphic/operation/index.ts @@ -0,0 +1 @@ +export * from './JlOperation'; diff --git a/src/jlgraphic/plugins/CommonMousePlugin.ts b/src/jlgraphic/plugins/CommonMousePlugin.ts new file mode 100644 index 0000000..aa2f2f6 --- /dev/null +++ b/src/jlgraphic/plugins/CommonMousePlugin.ts @@ -0,0 +1,574 @@ +import { + Container, + DisplayObject, + FederatedMouseEvent, + Graphics, + Point, +} from 'pixi.js'; +import { GraphicApp, JlCanvas } from '../app'; +import { JlGraphic } from '../core'; +import { AppInteractionPlugin } from './InteractionPlugin'; +import { AbsorbablePosition } from '../graphic'; +import { recursiveChildren } from '../utils'; + +export class GraphicDragEvent { + /** + * 原始事件 + */ + event: FederatedMouseEvent; + app: GraphicApp; + target: DisplayObject; + start: Point; + point: Point; + dx = 0; // 距上一个move点的x移动距离 + dy = 0; // 距上一个move点的y移动距离 + constructor( + app: GraphicApp, + event: FederatedMouseEvent, + target: DisplayObject, + start: Point, + point: Point, + dx?: number, + dy?: number + ) { + this.app = app; + this.event = event; + this.target = target; + this.start = start; + this.point = point; + if (dx) { + this.dx = dx; + } + if (dy) { + this.dy = dy; + } + } + // static buildJlGraphicDragEvent( + // graphic: JlGraphic, + // source: GraphicDragEvent + // ): GraphicDragEvent { + // const e = new GraphicDragEvent( + // source.app, + // source.event, + // source.target, + // source.start, + // source.point, + // source.dx, + // source.dy + // ); + // return e; + // } + + /** + * 距拖拽起始点的x方向距离 + */ + public get dsx(): number { + return this.point.x - this.start.x; + } + /** + * 距拖拽起始点的y方向距离 + */ + public get dsy(): number { + return this.point.y - this.start.y; + } +} + +type GraphicSelectFilter = (graphic: JlGraphic) => boolean; + +export interface IMouseToolOptions { + /** + * 是否启用框选,默认启用 + */ + boxSelect?: boolean; + /** + * 是否启用视口拖拽(默认右键),默认启用 + */ + viewportDrag?: boolean; + /** + * 是否启用鼠标滚轮缩放,默认启用 + */ + wheelZoom?: boolean; + /** + * boxSelect 移动多少像素触发, 默认3 + */ + threshold?: number; + /** + * 可选择图形过滤器 + */ + selectFilter?: GraphicSelectFilter; +} + +class CompleteMouseToolOptions implements IMouseToolOptions { + boxSelect: boolean; + viewportDrag: boolean; + wheelZoom: boolean; + threshold: number; + selectFilter?: GraphicSelectFilter | undefined; + constructor() { + this.boxSelect = true; + this.viewportDrag = true; + this.wheelZoom = true; + this.threshold = 3; + } + update(options: IMouseToolOptions) { + if (options.boxSelect != undefined) { + this.boxSelect = options.boxSelect; + } + if (options.viewportDrag != undefined) { + this.viewportDrag = options.viewportDrag; + } + if (options.wheelZoom != undefined) { + this.wheelZoom = options.wheelZoom; + } + if (options.threshold != undefined) { + this.threshold = options.threshold; + } + this.selectFilter = options.selectFilter; + } +} + +const DefaultSelectToolOptions: CompleteMouseToolOptions = + new CompleteMouseToolOptions(); + +/** + * 通用交互工具 + */ +export class CommonMouseTool extends AppInteractionPlugin { + static Name = 'mouse-tool'; + options: CompleteMouseToolOptions; + box: Graphics = new Graphics(); + leftDownTarget: DisplayObject | null = null; + start: Point | null = null; + last: Point | null = null; + drag = false; + graphicSelect = false; + + /** + * 可吸附位置列表 + */ + absorbablePositions?: AbsorbablePosition[]; + apContainer: Container; + static AbsorbablePosisiontsName = '__AbsorbablePosisionts'; + + constructor(graphicApp: GraphicApp) { + super(CommonMouseTool.Name, graphicApp); + this.options = DefaultSelectToolOptions; + this.apContainer = new Container(); + this.apContainer.name = CommonMouseTool.AbsorbablePosisiontsName; + this.app.canvas.addAssistantAppend(this.apContainer); + graphicApp.on('options-update', (options) => { + if (options.mouseToolOptions) { + this.options.update(options.mouseToolOptions); + if (this.isActive()) { + this.pause(); + this.resume(); + } + } + if (options.absorbablePositions) { + this.absorbablePositions = options.absorbablePositions; + } + }); + } + + bind(): void { + const canvas = this.app.canvas; + canvas.interactive = true; + canvas.on('mousedown', this.onMouseDown, this); + canvas.on('mouseup', this.onMouseUp, this); + if (this.options.viewportDrag) { + this.app.viewport.drag({ + mouseButtons: 'right', + }); + canvas.on('rightdown', this.setCursor, this); + canvas.on('rightup', this.resumeCursor, this); + canvas.on('rightupoutside', this.resumeCursor, this); + } + if (this.options.wheelZoom) { + this.app.viewport.wheel({ + percent: 0.01, + }); + } + } + unbind(): void { + const canvas = this.app.canvas; + // 确保所有事件取消监听 + canvas.off('mousedown', this.onMouseDown, this); + canvas.off('mouseup', this.onMouseUp, this); + canvas.off('mousemove', this.onDrag, this); + canvas.off('mouseup', this.onDragEnd, this); + canvas.off('mouseupoutside', this.onDragEnd, this); + this.app.viewport.plugins.remove('drag'); + canvas.off('rightdown', this.setCursor, this); + canvas.off('rightup', this.resumeCursor, this); + canvas.off('rightupoutside', this.resumeCursor, this); + + this.app.viewport.plugins.remove('wheel'); + this.clearCache(); + } + + setCursor() { + if (this.app.app.view.style) { + this.app.app.view.style.cursor = 'grab'; + } + } + + resumeCursor() { + if (this.app.app.view.style) { + this.app.app.view.style.cursor = 'inherit'; + } + } + + onMouseDown(e: FederatedMouseEvent) { + this.leftDownTarget = e.target as DisplayObject; + const canvas = this.app.canvas; + let enableDrag = false; + + this.graphicSelect = false; + if (e.target instanceof JlCanvas) { + // 画布 + if (this.options.boxSelect) { + // 框选 + enableDrag = true; + } + } else { + // 图形 + const graphic = (e.target as DisplayObject).getGraphic(); + if (graphic) { + const app = this.app; + // 图形选中 + if (!e.ctrlKey && !graphic.selected && graphic.selectable) { + app.updateSelected(graphic); + graphic.childEdit = false; + this.graphicSelect = true; + } else if ( + !e.ctrlKey && + graphic.selected && + graphic.childEdit && + this.leftDownTarget.isGraphicChild() + ) { + if (this.leftDownTarget.selectable) { + graphic.setChildSelected(this.leftDownTarget); + } else { + graphic.exitChildEdit(); + } + } + // 判断是否开启拖拽 + if ( + !this.leftDownTarget.isAssistantAppend() && + !graphic.draggable && + !this.leftDownTarget.draggable + ) { + console.debug( + '图形未开启拖拽,若需开启,设置draggable = true', + graphic, + this.leftDownTarget + ); + return; + } + } + enableDrag = true; + } + + if (enableDrag) { + // 初始化拖拽监听 + // console.log('drag 开始'); + this.start = this.app.toCanvasCoordinates(e.global); + canvas.on('mousemove', this.onDrag, this); + canvas.on('mouseupoutside', this.onDragEnd, this); + } + } + + /** + * 选中处理 + * @param e + */ + onMouseUp(e: FederatedMouseEvent) { + const app = this.app; + if (this.drag) { + this.onDragEnd(e); + } else { + const target = e.target as DisplayObject; + const graphic = (e.target as DisplayObject).getGraphic(); + if ( + graphic && + graphic.selected && + !this.graphicSelect && + app.selectedGraphics.length == 1 && + target === this.leftDownTarget && + target.isGraphicChild() && + target.selectable + ) { + graphic.childEdit = true; + } + if (e.ctrlKey) { + // 多选 + if (graphic) { + if (graphic.childEdit && target === this.leftDownTarget) { + graphic.invertChildSelected(target); + } else { + graphic.invertSelected(); + app.fireSelectedChange(graphic); + } + } + } else { + // 非多选 + if (e.target instanceof JlCanvas) { + this.app.updateSelected(); + } else { + if ( + graphic && + graphic.childEdit && + app.selectedGraphics.length === 1 && + target === this.leftDownTarget + ) { + graphic.setChildSelected(target); + } + } + } + // 多个图形选中,退出子元素编辑模式 + const selecteds = this.app.selectedGraphics; + if (selecteds.length > 1) { + selecteds.forEach((g) => g.exitChildEdit()); + } + } + this.clearCache(); + } + + /** + * 清理缓存 + */ + clearCache() { + this.start = null; + this.last = null; + this.drag = false; + this.leftDownTarget = null; + this.box.clear(); + this.app.canvas.removeChild(this.box); + } + + public get boxSelect(): boolean | undefined { + return this.options.boxSelect; + } + + public get selectFilter(): GraphicSelectFilter | undefined { + return this.options.selectFilter; + } + + public get threshold(): number { + return this.options.threshold; + } + + /** + * 框选图形绘制并检查 + */ + boxSelectDraw(start: Point, end: Point): void { + if (!this.drag) return; + // 绘制框选矩形框 + this.box.clear(); + this.box.lineStyle({ width: 1, color: 0x000000 }); + const dsx = end.x - start.x; + const dsy = end.y - start.y; + let { x, y } = start; + if (dsx < 0) { + x += dsx; + } + if (dsy < 0) { + y += dsy; + } + const width = Math.abs(dsx); + const height = Math.abs(dsy); + this.box.drawRect(x, y, width, height); + } + + /** + * 框选图形判断 + * @returns + */ + boxSelectGraphicCheck(): void { + if (!this.drag) return; + // 遍历筛选 + const boxRect = this.box.getLocalBounds(); + const app = this.app; + const selects: JlGraphic[] = []; + app.queryStore.getAllGraphics().forEach((g) => { + if (!this.selectFilter || this.selectFilter(g)) { + // 选择过滤器 + if (g.boxIntersectCheck(boxRect)) { + selects.push(g); + } + } + }); + app.updateSelected(...selects); + } + + onDrag(e: FederatedMouseEvent) { + if (this.start) { + const current = this.app.toCanvasCoordinates(e.global); + const dragStart = + Math.abs(current.x - this.start.x) > this.threshold || + Math.abs(current.y - this.start.y) > this.threshold; + if (this.leftDownTarget instanceof JlCanvas) { + // 画布drag + if (!this.drag && dragStart) { + this.drag = true; + this.app.canvas.addChild(this.box); + } + if (this.drag) { + this.boxSelectDraw(this.start, current); + // this.boxSelectGraphicCheck(); + } + } else { + // 图形drag + if (this.start && !this.drag && dragStart) { + // 启动拖拽 + if (this.leftDownTarget) { + const s = this.start; + const dragEvent = new GraphicDragEvent( + this.app, + e, + this.leftDownTarget, + s, + s + ); + this.handleDragEvent(dragEvent, 'dragstart'); + } + this.last = this.start.clone(); + this.drag = true; + } + + // drag移动处理 + if (this.drag && this.last && this.start) { + const current = this.app.toCanvasCoordinates(e.global); + const dx = current.x - this.last.x; + const dy = current.y - this.last.y; + if (this.leftDownTarget) { + const dragEvent = new GraphicDragEvent( + this.app, + e, + this.leftDownTarget, + this.start, + current, + dx, + dy + ); + this.handleDragEvent(dragEvent, 'dragmove'); + } + this.last = current; + } + } + } + } + /** + * drag操作结束处理 + * @param e + */ + onDragEnd(e: FederatedMouseEvent) { + if (this.leftDownTarget instanceof JlCanvas) { + this.boxSelectGraphicCheck(); + } else { + if (this.drag) { + const end = this.app.toCanvasCoordinates(e.global); + if (this.start && this.leftDownTarget) { + const dragEvent = new GraphicDragEvent( + this.app, + e, + this.leftDownTarget, + this.start, + end + ); + this.handleDragEvent(dragEvent, 'dragend'); + } + } + } + const canvas = this.app.canvas; + canvas.off('mousemove', this.onDrag, this); + canvas.off('mouseup', this.onDragEnd, this); + canvas.off('mouseupoutside', this.onDragEnd, this); + this.clearCache(); + } + + /** + * 处理拖拽事件(执行、发布) + * @param dragEvent + * @param type + */ + handleDragEvent( + dragEvent: GraphicDragEvent, + type: 'dragstart' | 'dragmove' | 'dragend' + ): void { + const graphic = dragEvent.target.getGraphic(); + const targets: DisplayObject[] = []; + if (!graphic) { + targets.push(dragEvent.target); + } else { + if ( + dragEvent.target.isGraphicChild() && + dragEvent.target.selected && + dragEvent.target.draggable + ) { + // 图形子元素 + recursiveChildren(graphic, (child) => { + if (child.selected && child.draggable) { + targets.push(child); + } + }); + } else { + // 图形对象 + targets.push(...this.app.selectedGraphics); + } + } + if (type === 'dragstart') { + targets.forEach((target) => { + target.dragStartPoint = target.position.clone(); + target.emit(type, dragEvent); + if (!target.isAssistantAppend()) { + target.emit('transformstart', target); + } + }); + // 显示吸附图形 + if (this.absorbablePositions && this.absorbablePositions.length > 0) { + this.apContainer.removeChildren(); + this.apContainer.addChild(...this.absorbablePositions); + } + } else { + // 处理位移 + targets.forEach((target) => { + if (target.dragStartPoint) { + target.position.set( + target.dragStartPoint.x + dragEvent.dsx, + target.dragStartPoint.y + dragEvent.dsy + ); + } + }); + // 处理吸附 + if (this.absorbablePositions) { + for (let i = 0; i < this.absorbablePositions.length; i++) { + const ap = this.absorbablePositions[i]; + if (ap.tryAbsorb(...targets)) { + break; + } + } + } + // 事件发布 + targets.forEach((target) => { + target.emit(type, dragEvent); + if (type === 'dragend' && !target.isAssistantAppend()) { + target.emit('transformend', target); + } + }); + + if (type === 'dragend') { + if (this.app.selectedGraphics.length == 1) { + this.app.selectedGraphics[0].repaint(); + } + targets.forEach((target) => { + target.dragStartPoint = null; + }); + // 移除吸附图形 + this.absorbablePositions = []; + this.apContainer.removeChildren(); + } + } + this.app.emit(type, dragEvent); + } +} diff --git a/src/jlgraphic/plugins/CopyPlugin.ts b/src/jlgraphic/plugins/CopyPlugin.ts new file mode 100644 index 0000000..17cc15f --- /dev/null +++ b/src/jlgraphic/plugins/CopyPlugin.ts @@ -0,0 +1,137 @@ +import { Container, FederatedPointerEvent, Point } from 'pixi.js'; +import { GraphicApp, GraphicCreateOperation } from '../app'; +import { JlGraphic } from '../core'; +import { KeyListener } from './KeyboardPlugin'; + +export class GraphicCopyPlugin { + container: Container; + app: GraphicApp; + keyListeners: KeyListener[]; + copys: JlGraphic[]; + start?: Point; + running = false; + moveLimit?: 'x' | 'y'; + constructor(app: GraphicApp) { + this.app = app; + this.container = new Container(); + this.copys = []; + this.keyListeners = []; + this.keyListeners.push( + new KeyListener({ + // ESC 用于取消复制操作 + value: 'Escape', + global: true, + // combinations: [CombinationKey.Ctrl], + onPress: () => { + this.cancle(); + }, + }), + new KeyListener({ + // X 限制只能在x轴移动 + value: 'KeyX', + global: true, + // combinations: [CombinationKey.Ctrl], + onPress: () => { + this.updateMoveLimit('x'); + }, + }), + new KeyListener({ + // Y 限制只能在y轴移动 + value: 'KeyY', + global: true, + // combinations: [CombinationKey.Ctrl], + onPress: () => { + this.updateMoveLimit('y'); + }, + }) + ); + } + + updateMoveLimit(limit?: 'x' | 'y'): void { + if (this.moveLimit === limit) { + this.moveLimit = undefined; + } else { + this.moveLimit = limit; + } + } + + init(): void { + if (this.running) return; + if (this.app.selectedGraphics.length === 0) { + throw new Error('没有选中图形,复制取消'); + } + this.running = true; + this.copys = []; + this.container.alpha = 0.5; + this.app.canvas.addChild(this.container); + const app = this.app; + this.app.selectedGraphics.forEach((g) => { + const template = app.getGraphicTemplatesByType(g.type); + const clone = template.clone(g); + this.copys.push(clone); + this.container.addChild(clone); + }); + this.app.canvas.on('mousemove', this.onPointerMove, this); + this.app.canvas.on('pointertap', this.onFinish, this); + this.keyListeners.forEach((kl) => { + this.app.addKeyboardListener(kl); + }); + } + + clear(): void { + this.running = false; + this.start = undefined; + this.moveLimit = undefined; + this.copys = []; + this.container.removeChildren(); + this.app.canvas.removeChild(this.container); + this.app.canvas.off('mousemove', this.onPointerMove, this); + this.app.canvas.off('pointertap', this.onFinish, this); + this.keyListeners.forEach((kl) => { + this.app.removeKeyboardListener(kl); + }); + } + + onPointerMove(e: FederatedPointerEvent): void { + const cp = this.app.toCanvasCoordinates(e.global); + if (!this.start) { + this.start = cp; + } else { + if (this.moveLimit === 'x') { + const dx = cp.x - this.start.x; + this.container.position.x = dx; + this.container.position.y = 0; + } else if (this.moveLimit === 'y') { + const dy = cp.y - this.start.y; + this.container.position.x = 0; + this.container.position.y = dy; + } else { + const dx = cp.x - this.start.x; + const dy = cp.y - this.start.y; + this.container.position.x = dx; + this.container.position.y = dy; + } + } + } + + onFinish(): void { + console.log('复制确认'); + // 将图形添加到app + this.copys.forEach((g) => { + g.position.x += this.container.position.x; + g.position.y += this.container.position.y; + }); + this.app.addGraphics(...this.copys); + // 创建图形对象操作记录 + this.app.opRecord.record(new GraphicCreateOperation(this.app, this.copys)); + this.app.detectRelations(); + this.app.updateSelected(...this.copys); + this.clear(); + } + + cancle(): void { + console.log('复制操作取消'); + this.app.canvas.removeChild(this.container); + this.clear(); + } +} diff --git a/src/jlgraphic/plugins/GraphicEditPlugin.ts b/src/jlgraphic/plugins/GraphicEditPlugin.ts new file mode 100644 index 0000000..22a7af7 --- /dev/null +++ b/src/jlgraphic/plugins/GraphicEditPlugin.ts @@ -0,0 +1,232 @@ +import { + Color, + Container, + DisplayObject, + Graphics, + IDestroyOptions, + IPointData, +} from 'pixi.js'; +import { JlGraphic } from '../core'; +import { DraggablePoint } from '../graphic'; +import { calculateMirrorPoint, distance2 } from '../utils'; +import { GraphicDragEvent } from './CommonMousePlugin'; + +export abstract class GraphicEditPlugin extends Container { + graphic: JlGraphic; + constructor(g: JlGraphic) { + super(); + this.graphic = g; + this.zIndex = 2; + this.graphic.on('transformstart', this.hideAll, this); + this.graphic.on('transformend', this.showAll, this); + this.graphic.on('repaint', this.showAll, this); + } + + destroy(options?: boolean | IDestroyOptions | undefined): void { + this.graphic.off('transformstart', this.hideAll, this); + this.graphic.off('transformend', this.showAll, this); + this.graphic.off('repaint', this.showAll, this); + super.destroy(options); + } + + abstract updateEditedPointsPosition(): void; + + hideAll() { + this.visible = false; + } + showAll() { + this.updateEditedPointsPosition(); + this.visible = true; + } +} + +export abstract class LineEditPlugin extends GraphicEditPlugin { + linePoints: IPointData[]; + editedPoints: DraggablePoint[] = []; + onEditPointCreate?: onEditPointCreate; + constructor( + g: JlGraphic, + linePoints: IPointData[], + onEditPointCreate?: onEditPointCreate + ) { + super(g); + this.linePoints = linePoints; + this.onEditPointCreate = onEditPointCreate; + this.graphic.on('pointupdate', this.reset, this); + } + + destroy(options?: boolean | IDestroyOptions | undefined): void { + this.graphic.off('pointupdate', this.reset, this); + super.destroy(options); + } + + reset(_obj: DisplayObject, points: IPointData[]): void { + this.linePoints = points; + this.removeChildren(); + this.editedPoints.splice(0, this.editedPoints.length); + this.initEditPoints(); + } + + abstract initEditPoints(): void; +} + +export type onEditPointCreate = ( + g: JlGraphic, + dp: DraggablePoint, + index: number +) => void; +/** + * 折线编辑(兼容线段) + */ +export class PolylineEditPlugin extends LineEditPlugin { + static Name = 'line_points_edit'; + constructor( + g: JlGraphic, + linePoints: IPointData[], + onEditPointCreate?: onEditPointCreate + ) { + super(g, linePoints, onEditPointCreate); + this.name = PolylineEditPlugin.Name; + this.initEditPoints(); + } + + initEditPoints() { + const cps = this.graphic.localToCanvasPoints(...this.linePoints); + for (let i = 0; i < cps.length; i++) { + const p = cps[i]; + const dp = new DraggablePoint(p); + dp.on('dragmove', () => { + const tlp = this.graphic.canvasToLocalPoint(dp.position); + const cp = this.linePoints[i]; + cp.x = tlp.x; + cp.y = tlp.y; + this.graphic.repaint(); + }); + if (this.onEditPointCreate) { + this.onEditPointCreate(this.graphic, dp, i); + } + this.editedPoints.push(dp); + } + this.addChild(...this.editedPoints); + } + updateEditedPointsPosition() { + const cps = this.graphic.localToCanvasPoints(...this.linePoints); + if (cps.length === this.editedPoints.length) { + for (let i = 0; i < cps.length; i++) { + const cp = cps[i]; + this.editedPoints[i].position.copyFrom(cp); + } + } + } +} + +/** + * 贝塞尔曲线编辑 + */ +export class BezierCurveEditPlugin extends LineEditPlugin { + static Name = 'bezier_curve_points_edit'; + // 曲线控制点辅助线 + auxiliaryLines: Graphics[] = []; + constructor( + g: JlGraphic, + linePoints: IPointData[], + onEditPointCreate?: onEditPointCreate + ) { + super(g, linePoints, onEditPointCreate); + this.name = BezierCurveEditPlugin.Name; + this.initEditPoints(); + } + + reset(_obj: DisplayObject, points: IPointData[]): void { + this.auxiliaryLines.splice(0, this.auxiliaryLines.length); + super.reset(_obj, points); + } + + initEditPoints() { + const cps = this.graphic.localToCanvasPoints(...this.linePoints); + for (let i = 0; i < cps.length; i++) { + const p = cps[i]; + const dp = new DraggablePoint(p); + const startOrEnd = i == 0 || i == cps.length - 1; + const c = i % 3; + if (c === 1) { + const fp = cps[i - 1]; + const line = new Graphics(); + this.drawAuxiliaryLine(line, fp, p); + this.auxiliaryLines.push(line); + } else if (c === 2) { + const np = cps[i + 1]; + const line = new Graphics(); + this.drawAuxiliaryLine(line, p, np); + this.auxiliaryLines.push(line); + } + dp.on('dragmove', (de: GraphicDragEvent) => { + const tlp = this.graphic.canvasToLocalPoint(dp.position); + const cp = this.linePoints[i]; + cp.x = tlp.x; + cp.y = tlp.y; + if (c === 0 && !startOrEnd) { + const fp = this.linePoints[i - 1]; + const np = this.linePoints[i + 1]; + fp.x = fp.x + de.dx; + fp.y = fp.y + de.dy; + np.x = np.x + de.dx; + np.y = np.y + de.dy; + } else if (c === 1 && i !== 1) { + const bp = this.linePoints[i - 1]; + const fp2 = this.linePoints[i - 2]; + const distance = distance2(bp, fp2); + const mp = calculateMirrorPoint(bp, cp, distance); + fp2.x = mp.x; + fp2.y = mp.y; + } else if (c === 2 && i !== cps.length - 2) { + const bp = this.linePoints[i + 1]; + const np2 = this.linePoints[i + 2]; + const distance = distance2(bp, np2); + const mp = calculateMirrorPoint(bp, cp, distance); + np2.x = mp.x; + np2.y = mp.y; + } + this.graphic.repaint(); + }); + if (this.onEditPointCreate) { + this.onEditPointCreate(this.graphic, dp, i); + } + this.editedPoints.push(dp); + if (this.auxiliaryLines.length > 0) { + this.addChild(...this.auxiliaryLines); + } + } + this.addChild(...this.editedPoints); + } + + drawAuxiliaryLine(line: Graphics, p1: IPointData, p2: IPointData) { + line.clear(); + line.lineStyle(1, new Color('blue')); + line.moveTo(p1.x, p1.y); + line.lineTo(p2.x, p2.y); + } + + updateEditedPointsPosition() { + const cps = this.graphic.localToCanvasPoints(...this.linePoints); + if (cps.length === this.editedPoints.length) { + for (let i = 0; i < cps.length; i++) { + const cp = cps[i]; + this.editedPoints[i].position.copyFrom(cp); + const c = i % 3; + const d = Math.floor(i / 3); + if (c === 1 || c === 2) { + const li = d * 2 + c - 1; + const line = this.auxiliaryLines[li]; + if (c === 1) { + const fp = cps[i - 1]; + this.drawAuxiliaryLine(line, fp, cp); + } else { + const np = cps[i + 1]; + this.drawAuxiliaryLine(line, cp, np); + } + } + } + } + } +} diff --git a/src/jlgraphic/plugins/InteractionPlugin.ts b/src/jlgraphic/plugins/InteractionPlugin.ts new file mode 100644 index 0000000..47d0ad0 --- /dev/null +++ b/src/jlgraphic/plugins/InteractionPlugin.ts @@ -0,0 +1,243 @@ +import { FederatedMouseEvent } from 'pixi.js'; +import { GraphicApp } from '../app/JlGraphicApp'; +import { JlGraphic } from '../core/JlGraphic'; + +export enum InteractionPluginType { + App = 'app', + Graphic = 'graphic', + Other = 'other', +} + +/** + * 交互插件 + */ +export interface InteractionPlugin { + readonly _type: string; + name: string; // 唯一标识 + app: GraphicApp; + + /** + * 停止 + */ + pause(): void; + /** + * 恢复 + */ + resume(): void; + /** + * 是否生效 + */ + isActive(): boolean; + /** + * 关闭绑定的交互事件,清理插件相关数据(如需要),从app中移除 + */ + destroy(): void; +} + +export class ViewportMovePlugin implements InteractionPlugin { + static Name = '__viewport-move'; + readonly _type = InteractionPluginType.Other; + name = ViewportMovePlugin.Name; // 唯一标识 + app: GraphicApp; + + moveHandler: NodeJS.Timeout | null = null; + moveSpeedx = 0; + moveSpeedy = 0; + + _pause: boolean; + + constructor(app: GraphicApp) { + this.app = app; + this._pause = true; + app.registerInteractionPlugin(this); + } + + static new(app: GraphicApp): ViewportMovePlugin { + return new ViewportMovePlugin(app); + } + + resume(): void { + this.app.canvas.on('pointermove', this.viewportMove, this); + } + + pause(): void { + this.app.canvas.off('pointermove', this.viewportMove, this); + } + + isActive(): boolean { + return !this._pause; + } + destroy(): void { + this.pause(); + this.app.removeInteractionPlugin(this); + } + + startMove(moveSpeedx: number, moveSpeedy: number) { + this.moveSpeedx = moveSpeedx; + this.moveSpeedy = moveSpeedy; + if (this.moveHandler == null) { + const viewport = this.app.viewport; + this.moveHandler = setInterval(() => { + viewport.moveCorner( + viewport.corner.x + this.moveSpeedx, + viewport.corner.y + this.moveSpeedy + ); + }, 50); + } + } + + stopMove() { + if (this.moveHandler != null) { + clearInterval(this.moveHandler); + this.moveHandler = null; + } + } + + viewportMove(e: FederatedMouseEvent) { + const sp = e.global; + const viewport = this.app.viewport; + const range = 50; // 触发范围和移动速度范围 + let moveSpeedx = 0; + let moveSpeedy = 0; + const ds = 3; // 移动速度减小比例控制 + if (sp.x < range) { + moveSpeedx = sp.x - range; + } else if (sp.x > viewport.screenWidth - range) { + moveSpeedx = sp.x + range - viewport.screenWidth; + } else { + moveSpeedx = 0; + } + if (sp.y < range) { + moveSpeedy = sp.y - range; + } else if (sp.y > viewport.screenHeight - range) { + moveSpeedy = sp.y + range - viewport.screenHeight; + } else { + moveSpeedy = 0; + } + if (moveSpeedx == 0 && moveSpeedy == 0) { + this.app.canvas.cursor = 'auto'; + this.stopMove(); + } else { + this.app.canvas.cursor = 'grab'; + this.startMove(moveSpeedx / ds, moveSpeedy / ds); + } + } +} + +/** + * 应用交互插件,同时只能生效一个 + */ +export abstract class AppInteractionPlugin implements InteractionPlugin { + readonly _type = InteractionPluginType.App; + name: string; // 唯一标识 + app: GraphicApp; + _pause: boolean; + constructor(name: string, app: GraphicApp) { + this.name = name; + this.app = app; + this._pause = true; + app.registerInteractionPlugin(this); + } + isActive(): boolean { + return !this._pause; + } + + pause(): void { + this.unbind(); + this._pause = true; + } + + /** + * 恢复,app交互插件同时只能生效一个 + */ + resume(): void { + this.app.pauseAppInteractionPlugins(); + this.bind(); + this._pause = false; + } + + abstract bind(): void; + abstract unbind(): void; + + destroy(): void { + this.pause(); + this.app.removeInteractionPlugin(this); + } +} + +/** + * 图形交互插件,可同时生效 + */ +export abstract class GraphicInteractionPlugin + implements InteractionPlugin +{ + readonly _type = InteractionPluginType.Graphic; + app: GraphicApp; + name: string; // 唯一标识 + _pause: boolean; + constructor(name: string, app: GraphicApp) { + this.app = app; + this.name = name; + this._pause = true; + app.registerInteractionPlugin(this); + this.resume(); + // 新增的图形对象绑定 + this.app.on('graphicstored', (g) => { + if (this.isActive()) { + this.binds(this.filter(g)); + } + }); + this.app.on('graphicdeleted', (g) => { + if (this.isActive()) { + this.unbinds(this.filter(g)); + } + }); + } + + isActive(): boolean { + return !this._pause; + } + + pause(): void { + const list = this.filter(...this.app.queryStore.getAllGraphics()); + this.unbinds(list); + this._pause = true; + } + + resume(): void { + const list = this.filter(...this.app.queryStore.getAllGraphics()); + this.binds(list); + this._pause = false; + } + + /** + * 过滤需要的图形对象 + */ + abstract filter(...grahpics: JlGraphic[]): G[] | undefined; + + binds(list?: G[]): void { + if (list) { + list.forEach((g) => this.bind(g)); + } + } + unbinds(list?: G[]): void { + if (list) { + list.forEach((g) => this.unbind(g)); + } + } + /** + * 绑定图形对象的交互处理 + * @param g 图形对象 + */ + abstract bind(g: G): void; + /** + * 取消图形对象的交互处理 + * @param g 图形对象 + */ + abstract unbind(g: G): void; + + destroy(): void { + this.pause(); + this.app.removeInteractionPlugin(this); + } +} diff --git a/src/jlgraphic/plugins/KeyboardPlugin.ts b/src/jlgraphic/plugins/KeyboardPlugin.ts new file mode 100644 index 0000000..afa97d5 --- /dev/null +++ b/src/jlgraphic/plugins/KeyboardPlugin.ts @@ -0,0 +1,346 @@ +import { GraphicApp } from '../app/JlGraphicApp'; + +let target: Node | undefined; + +export class GlobalKeyboardHelper { + appKeyboardPluginMap: JlGraphicAppKeyboardPlugin[] = []; + + constructor() { + window.onkeydown = (e: KeyboardEvent) => { + this.appKeyboardPluginMap.forEach((plugin) => { + const listenerMap = plugin.getKeyListener(e); + listenerMap?.forEach((listener) => { + if (listener.global) { + listener.press(e, plugin.app); + } + }); + }); + if (e.ctrlKey) { + if (e.code == 'KeyS') { + // 屏蔽全局Ctrl+S保存操作 + // console.log('屏蔽全局Ctrl+S') + return false; + } + } + if (target && target.nodeName == 'CANVAS') { + // 事件的目标是画布时,屏蔽总的键盘操作操作 + if (e.ctrlKey) { + if (e.code == 'KeyA' || e.code == 'KeyS') { + // 屏蔽Canvas上的Ctrl+A、Ctrl+S操作 + return false; + } + } + } + return true; + }; + window.onkeyup = (e: KeyboardEvent) => { + this.appKeyboardPluginMap.forEach((plugin) => { + const listenerMap = plugin.getKeyListener(e); + listenerMap?.forEach((listener) => { + if (listener.global) { + listener.release(e, plugin.app); + } + }); + }); + }; + } + + registerGAKPlugin(plugin: JlGraphicAppKeyboardPlugin) { + if (!this.appKeyboardPluginMap.find((pg) => pg == plugin)) { + this.appKeyboardPluginMap.push(plugin); + } + } + + removeGAKPlugin(plugin: JlGraphicAppKeyboardPlugin) { + const index = this.appKeyboardPluginMap.findIndex((pg) => pg == plugin); + if (index >= 0) { + this.appKeyboardPluginMap.splice(index, 1); + } + } +} + +const GlobalKeyboardPlugin = new GlobalKeyboardHelper(); + +export class JlGraphicAppKeyboardPlugin { + app: GraphicApp; + /** + * 结构为Map> + */ + keyListenerMap: Map> = new Map< + number | string, + Map + >(); // 键值监听map + keyListenerStackMap: Map = new Map< + string, + KeyListener[] + >(); // 键值监听栈(多次注册相同的监听会把之前注册的监听器入栈,移除最新的监听会从栈中弹出一个作为指定事件监听处理器) + + constructor(app: GraphicApp) { + this.app = app; + GlobalKeyboardPlugin.registerGAKPlugin(this); + const onMouseUpdateTarget = (e: MouseEvent) => { + const node = e.target as Node; + target = node; + // console.log('Mousedown Event', node.nodeName, node.nodeType, node.nodeValue) + }; + const keydownHandle = (e: KeyboardEvent) => { + // console.log(e.key, e.code, e.keyCode); + if (target && target == this.app.dom.getElementsByTagName('canvas')[0]) { + const listenerMap = this.getKeyListener(e); + listenerMap?.forEach((listener) => { + if (!listener.global) { + listener.press(e, this.app); + } + }); + } + }; + const keyupHandle = (e: KeyboardEvent) => { + if (target && target == this.app.dom.getElementsByTagName('canvas')[0]) { + const listenerMap = this.getKeyListener(e); + listenerMap?.forEach((listener) => { + if (!listener.global) { + listener.release(e, this.app); + } + }); + } + }; + + document.addEventListener('mousedown', onMouseUpdateTarget, false); + document.addEventListener('keydown', keydownHandle, false); + document.addEventListener('keyup', keyupHandle, false); + this.app.on('destroy', () => { + document.removeEventListener('mousedown', onMouseUpdateTarget, false); + document.removeEventListener('keydown', keydownHandle, false); + document.removeEventListener('keyup', keyupHandle, false); + }); + } + + private getOrInit(key: string | number): Map { + let map = this.keyListenerMap.get(key); + if (map === undefined) { + map = new Map(); + this.keyListenerMap.set(key, map); + } + return map; + } + + private getOrInitStack(key: string): KeyListener[] { + let stack = this.keyListenerStackMap.get(key); + if (stack === undefined) { + stack = []; + this.keyListenerStackMap.set(key, stack); + } + return stack; + } + + /** + * 注册按键监听,若有旧的,旧的入栈 + * @param keyListener + */ + addKeyListener(keyListener: KeyListener) { + const map = this.getOrInit(keyListener.value); + // 查询是否有旧的监听,若有入栈 + const old = map.get(keyListener.identifier); + if (old) { + const stack = this.getOrInitStack(keyListener.identifier); + stack.push(old); + } + map.set(keyListener.identifier, keyListener); + // console.log(this.getAllListenedKeys()); + } + /** + * 移除按键监听,若是当前注册的监听,尝试从栈中取出作为按键监听器,若是旧的,则同时移除栈中的监听 + * @param keyListener + */ + removeKeyListener(keyListener: KeyListener) { + const map = this.getOrInit(keyListener.value); + const old = map.get(keyListener.identifier); + map.delete(keyListener.identifier); + const stack = this.getOrInitStack(keyListener.identifier); + if (old && old === keyListener) { + // 是旧的监听 + const listener = stack.pop(); + if (listener) { + map.set(keyListener.identifier, listener); + } + } else { + // 移除栈中的 + const index = stack.findIndex((ls) => ls === keyListener); + if (index >= 0) { + stack.splice(index, 1); + } + } + // console.log(this); + } + + getKeyListenerBy(key: string | number): Map | undefined { + return this.keyListenerMap.get(key); + } + + getKeyListener(e: KeyboardEvent): Map | undefined { + return ( + this.getKeyListenerBy(e.key) || + this.getKeyListenerBy(e.code) || + this.getKeyListenerBy(e.keyCode) + ); + } + + isKeyListened(key: string | number): boolean { + return this.getOrInit(key).size > 0; + } + + /** + * 获取所有注册监听的键值(组合键) + */ + getAllListenedKeys(): string[] { + const keys: string[] = []; + this.keyListenerMap.forEach((v) => + v.forEach((_listener, ck) => keys.push(ck)) + ); + return keys; + } +} + +type KeyboardKeyHandler = (e: KeyboardEvent, app: GraphicApp) => void; + +export enum CombinationKey { + Ctrl = 'Ctrl', + Alt = 'Alt', + Shift = 'Shift', +} +export interface KeyListenerOptions { + // 具体的键值,可以是key/code/keycode(keycode已经弃用,建议优先使用key或code),例如:KeyA/(a/A)分别表示键盘A建,其中KeyA为键盘事件的code字段,a/A为键盘事件的key字段 + value: string | number; + // 组合键 + combinations?: CombinationKey[]; + // 是否监听全局,为false则只在画布为焦点时才处理 + global?: boolean; + // 按下操作处理 + onPress?: KeyboardKeyHandler; + // 按下操作是否每次触发,默认一次 + pressTriggerEveryTime?: boolean; + // 释放/抬起操作处理 + onRelease?: KeyboardKeyHandler; +} + +export interface ICompleteKeyListenerOptions { + // 具体的键值,可以是key/code/keycode(keycode已经弃用,建议优先使用key或code),例如:KeyA/(a/A)分别表示键盘A建,其中KeyA为键盘事件的code字段,a/A为键盘事件的key字段 + value: string | number; + // 组合键 + combinations: CombinationKey[]; + // 是否监听全局,为false则只在画布为焦点时才处理 + global: boolean; + // 按下操作处理 + onPress?: KeyboardKeyHandler; + pressTriggerEveryTime: boolean; + // 释放/抬起操作处理 + onRelease?: KeyboardKeyHandler; +} + +const DefaultKeyListenerOptions: ICompleteKeyListenerOptions = { + value: '', + combinations: [], + global: false, + onPress: undefined, + pressTriggerEveryTime: false, + onRelease: undefined, +}; + +export class KeyListener { + // value 支持keyCode,key,code三种值 + readonly options: ICompleteKeyListenerOptions; + private isPress = false; + + constructor(options: KeyListenerOptions) { + this.options = Object.assign({}, DefaultKeyListenerOptions, options); + } + + static create(options: KeyListenerOptions): KeyListener { + return new KeyListener(options); + } + + public get value(): string | number { + return this.options.value; + } + + public get combinations(): string[] { + return this.options.combinations; + } + + public get identifier(): string { + return this.options.combinations.join('+') + '+' + this.options.value; + } + + public get global(): boolean | undefined { + return this.options.global; + } + + public get onPress(): KeyboardKeyHandler | undefined { + return this.options.onPress; + } + + public set onPress(v: KeyboardKeyHandler | undefined) { + this.options.onPress = v; + } + + public get onRelease(): KeyboardKeyHandler | undefined { + return this.options.onRelease; + } + + public set onRelease(v: KeyboardKeyHandler | undefined) { + this.options.onRelease = v; + } + + public get pressTriggerEveryTime(): boolean { + return this.options.pressTriggerEveryTime; + } + + public set pressTriggerEveryTime(v: boolean) { + this.options.pressTriggerEveryTime = v; + } + + press(e: KeyboardEvent, app: GraphicApp): void { + if (!this.checkCombinations(e)) { + console.debug('组合键不匹配, 不执行press', e, this); + return; + } + if (this.pressTriggerEveryTime || !this.isPress) { + // console.log('Keydown: ', e, this.onPress); + this.isPress = true; + if (this.onPress) { + this.onPress(e, app); + } + } + } + /** + * 检查组合键是否匹配 + */ + checkCombinations(e: KeyboardEvent): boolean { + const cbs = this.combinations; + if (cbs.length > 0) { + if ( + ((e.altKey && cbs.includes(CombinationKey.Alt)) || + (!e.altKey && !cbs.includes(CombinationKey.Alt))) && + ((e.ctrlKey && cbs.includes(CombinationKey.Ctrl)) || + (!e.ctrlKey && !cbs.includes(CombinationKey.Ctrl))) && + ((e.shiftKey && cbs.includes(CombinationKey.Shift)) || + (!e.shiftKey && !cbs.includes(CombinationKey.Shift))) + ) { + return true; + } + } else { + return !e.altKey && !e.ctrlKey && !e.shiftKey; + } + return false; + } + + release(e: KeyboardEvent, app: GraphicApp): void { + if (this.isPress) { + // console.log('Keyup : ', e.key, e); + this.isPress = false; + if (this.onRelease) { + this.onRelease(e, app); + } + } + } +} diff --git a/src/jlgraphic/plugins/index.ts b/src/jlgraphic/plugins/index.ts new file mode 100644 index 0000000..2b6e07e --- /dev/null +++ b/src/jlgraphic/plugins/index.ts @@ -0,0 +1,4 @@ +export * from './InteractionPlugin'; +export * from './CommonMousePlugin'; +export * from './KeyboardPlugin'; +export * from './CopyPlugin'; diff --git a/src/jlgraphic/ui/ContextMenu.ts b/src/jlgraphic/ui/ContextMenu.ts new file mode 100644 index 0000000..da0936e --- /dev/null +++ b/src/jlgraphic/ui/ContextMenu.ts @@ -0,0 +1,668 @@ +import { Container, Graphics, Point, Rectangle, Text, utils } from 'pixi.js'; +import { GraphicApp } from '../app'; +import { OutOfBound } from '../utils'; +import { + DefaultWhiteMenuOptions, + MenuCompletionItemStyle, + MenuCompletionOptions, + MenuCompletionStyleOptions, + MenuGroupOptions, + MenuItemOptions, + MenuOptions, +} from './Menu'; + +export class ContextMenuPlugin { + app: GraphicApp; + contextMenuMap: Map = new Map(); + + constructor(app: GraphicApp) { + this.app = app; + } + /** + * 添加菜单 + */ + addContextMenu(menu: ContextMenu) { + if (this.contextMenuMap.has(menu.menuName)) { + console.log(`已经存在name=${menu.menuName}的菜单`); + } + this.contextMenuMap.set(menu.menuName, menu); + menu.plugin = this; + } + /** + * 通过菜单名称打开菜单到指定坐标 + * @param menuName + * @param global + */ + openMenu(menuName: string, global: Point) { + const menu = this.contextMenuMap.get(menuName); + if (menu) { + this.open(menu, global); + } else { + console.warn(`不存在name=${menuName}的菜单`); + } + } + /** + * 通过菜单名称关闭菜单 + * @param menuName + */ + closeMenu(menuName: string) { + const menu = this.contextMenuMap.get(menuName); + if (menu) { + this.close(menu); + } else { + console.warn(`不存在name=${menuName}的菜单`); + } + } + + /** + * 获取视口屏幕宽度 + */ + public get screenWidth(): number { + return this.app.viewport.screenWidth; + } + + /** + * 获取视口屏幕高度 + */ + public get screenHeight(): number { + return this.app.viewport.screenHeight; + } + + /** + * 打开菜单 + * @param menu + * @param global + */ + open(menu: ContextMenu, global: Point) { + if (!menu.opened) { + menu.opened = true; + this.app.app.stage.addChild(menu); + } + // 处理超出显示范围 + const screenHeight = this.screenHeight; + const bottomY = global.y + menu.height; + + let oob = this.oob(menu, global); + const pos = global.clone(); + if (oob.right) { + pos.x = global.x - menu.width; + } + if (oob.bottom) { + const py = global.y - menu.height; + if (py > 0) { + pos.y = py; + } else { + pos.y = global.y - (bottomY - screenHeight); + } + } + // 移动后是否左上超出 + oob = this.oob(menu, pos); + if (oob.left) { + pos.x = 0; + } + if (oob.top) { + pos.y = 0; + } + menu.position.copyFrom(pos); + } + /** + * 关闭菜单 + * @param menu + */ + close(menu: ContextMenu) { + if (menu.opened) { + menu.opened = false; + this.app.app.stage.removeChild(menu); + } + } + /** + * 越界检查 + * @param menu + * @param global + * @returns + */ + oob(menu: ContextMenu, global: Point): OutOfBound { + const screenWidth = this.screenWidth; + const screenHeight = this.screenHeight; + const bound = new Rectangle(0, 0, screenWidth, screenHeight); + const menuRect = new Rectangle(global.x, global.y, menu.width, menu.height); + return OutOfBound.check(menuRect, bound); + } +} + +/** + * 上下文菜单 + */ +export class ContextMenu extends Container { + parentMenuItem?: ContextMenuItem; + openedSubMenu?: ContextMenu; + menuOptions: MenuCompletionOptions; + _plugin?: ContextMenuPlugin; + opened = false; + bg: Graphics; + title?: Text; + groups: MenuGroup[]; + private padding = 5; + _active = false; // 激活状态 + timeoutCloseHandle?: NodeJS.Timeout; + + constructor(menuOptions: MenuOptions, parentMenuItem?: ContextMenuItem) { + super(); + this.menuOptions = Object.assign({}, DefaultWhiteMenuOptions, menuOptions); + this.bg = new Graphics(); + this.groups = []; + this.init(); + this.parentMenuItem = parentMenuItem; + } + + public get style(): MenuCompletionStyleOptions { + return this.menuOptions.style; + } + + public get parentMenu(): ContextMenu | undefined { + return this.parentMenuItem?.menu; + } + + public get rootMenu(): ContextMenu { + if (this.parentMenu) { + return this.parentMenu.rootMenu; + } + return this; + } + + /** + * 是否存在激活的菜单项 + * @returns + */ + hasActiveItem(): boolean { + for (let i = 0; i < this.groups.length; i++) { + const group = this.groups[i]; + if (group.hasActiveItem()) { + return true; + } + } + return false; + } + + public get active(): boolean { + return ( + this._active || + this.hasActiveItem() || + (this.parentMenuItem != undefined && this.parentMenuItem._active) + ); + } + + public set active(v: boolean) { + this._active = v; + this.onActiveChanged(); + } + + onActiveChanged() { + if (this.parentMenuItem) { + this.parentMenuItem.onActiveChanged(); + if (!this.active) { + this.timeoutCloseHandle = setTimeout(() => { + this.close(); + }, 500); + } else { + if (this.timeoutCloseHandle) { + clearTimeout(this.timeoutCloseHandle); + } + } + } + } + + setOptions(menuOptions: MenuOptions) { + this.menuOptions = Object.assign({}, DefaultWhiteMenuOptions, menuOptions); + this.init(); + } + + /** + * 初始化 + */ + init() { + // this.initTitle(); + this.groups = []; + const options = this.menuOptions; + let maxItemWidth = 0; + let borderHeight = 0; + options.groups.forEach(group => { + const menuGroup = new MenuGroup(this, group); + this.groups.push(menuGroup); + borderHeight += menuGroup.totalHeight; + if (menuGroup.maxWidth > maxItemWidth) { + maxItemWidth = menuGroup.maxWidth; + } + }); + + const splitLineWidth = 1; + + const bgWidth = maxItemWidth + this.padding * 2; + const bgHeight = + borderHeight + + this.groups.length * this.padding * 2 + + (this.groups.length - 1) * splitLineWidth; + + if (options.style.border) { + this.bg.lineStyle( + options.style.borderWidth, + utils.string2hex(options.style.borderColor) + ); + } + this.bg.beginFill(utils.string2hex(options.style.backgroundColor)); + if (options.style.borderRoundRadius > 0) { + this.bg.drawRoundedRect( + 0, + 0, + bgWidth, + bgHeight, + options.style.borderRoundRadius + ); + } else { + this.bg.drawRect(0, 0, bgWidth, bgHeight); + } + this.bg.endFill(); + let groupHeight = 0; + this.bg.lineStyle( + splitLineWidth, + utils.string2hex(options.style.borderColor) + ); + for (let i = 0; i < this.groups.length; i++) { + const group = this.groups[i]; + group.updateItemBox(maxItemWidth); + group.position.set(this.padding, groupHeight + this.padding); + if (i === this.groups.length - 1) { + // 最后一个 + break; + } + const splitLineY = groupHeight + group.height + this.padding * 2; + this.bg.moveTo(0, splitLineY); + this.bg.lineTo(bgWidth, splitLineY); + groupHeight = splitLineY + splitLineWidth; + } + + this.addChild(this.bg); + this.addChild(...this.groups); + + this.interactive = true; + this.on('pointerover', () => { + this.active = true; + }); + this.on('pointerout', () => { + this.active = false; + }); + } + + initTitle() { + if (this.menuOptions.title) { + this.title = new Text(this.menuOptions.title, { align: 'left' }); + } + } + + public get menuName(): string { + return this.menuOptions.name; + } + + public get plugin(): ContextMenuPlugin { + if (this.parentMenu) { + return this.parentMenu.plugin; + } + if (this._plugin) { + return this._plugin; + } + throw new Error(`上下文菜单name=${this.menuOptions.name}没有添加到插件中`); + } + + public set plugin(v: ContextMenuPlugin) { + this._plugin = v; + } + + /** + * 显示菜单 + */ + open(global: Point): void { + if (this.parentMenu) { + this.parentMenu.openSub(this, global); + } else { + this.plugin.open(this, global); + } + } + + /** + * 关闭菜单 + */ + close(): void { + if (this.openedSubMenu) { + this.openedSubMenu.close(); + this.openedSubMenu = undefined; + } + this.plugin.close(this); + } + + /** + * 打开子菜单 + * @param subMenu + * @param global + */ + private openSub(subMenu: ContextMenu, global: Point) { + if (this.openedSubMenu) { + this.openedSubMenu.close(); + } + const pos = global.clone(); + const oob = this.plugin.oob(subMenu, global); + if (oob.right) { + pos.x = this.position.x - subMenu.width + this.padding; + } + if (oob.bottom) { + pos.y = this.plugin.screenHeight - subMenu.height - this.padding; + } + this.plugin.open(subMenu, pos); + this.openedSubMenu = subMenu; + } +} + +class MenuGroup extends Container { + private gutter = 3; // 名称、快捷键、箭头文本间隙 + config: MenuGroupOptions; + menu: ContextMenu; + items: ContextMenuItem[] = []; + constructor(menu: ContextMenu, config: MenuGroupOptions) { + super(); + this.config = config; + this.menu = menu; + config.items.forEach(item => { + this.items.push(new ContextMenuItem(menu, item)); + }); + for (let i = 0; i < this.items.length; i++) { + const item = this.items[i]; + item.position.y = i * item.totalHeight; + } + this.addChild(...this.items); + } + + hasActiveItem(): boolean { + for (let i = 0; i < this.items.length; i++) { + const item = this.items[i]; + if (item.active) { + return true; + } + } + return false; + } + + public get maxWidth(): number { + const maxNameWidth = this.items + .map(item => item.nameBounds.width) + .sort() + .reverse()[0]; + const maxShortcutWidth = this.items + .map(item => item.shortcutKeyBounds.width) + .sort() + .reverse()[0]; + const maxWidth = + maxNameWidth + + this.gutter + + maxShortcutWidth + + this.items[0].paddingLeft + + this.items[0].paddingRight; + return maxWidth; + } + + public get totalHeight(): number { + let total = 0; + this.items.forEach(item => (total += item.totalHeight)); + return total; + } + + updateItemBox(maxItemWidth: number) { + this.items.forEach(item => item.updateBox(maxItemWidth, item.totalHeight)); + } +} + +/** + * 菜单项 + */ +class ContextMenuItem extends Container { + menu: ContextMenu; + config: MenuItemOptions; + /** + * 名称文本 + */ + nameText?: Text; + /** + * 快捷键文本 + */ + shortcutKeyText?: Text; + private gutter = 3; // 名称、快捷键、箭头文本间隙 + arrowText?: Text; + box: Graphics; + subMenu?: ContextMenu; + _active = false; // 激活状态 + + constructor(menu: ContextMenu, config: MenuItemOptions) { + super(); + this.menu = menu; + this.config = config; + this.box = new Graphics(); + this.addChild(this.box); + + this.initNameText(); + this.initShortcutKeyText(); + this.initSubMenu(); + } + + public get active(): boolean { + return this._active || (this.subMenu != undefined && this.subMenu.active); + } + + public set active(v: boolean) { + this._active = v; + if (this.subMenu) { + this.subMenu.onActiveChanged(); + } + this.onActiveChanged(); + } + + onActiveChanged() { + if (this.active) { + this.box.alpha = 1; + } else { + this.box.alpha = 0; + } + } + + public get textWidth(): number { + return this.nameBounds.width + this.shortcutKeyBounds.width + this.gutter; + } + + public get nameGraphic(): Text { + if (this.nameText) { + return this.nameText; + } + throw new Error(`菜单项name=${this.config.name}没有初始化名称图形对象`); + } + + public get totalHeight(): number { + return this.paddingTop + this.paddingBottom + this.nameGraphic.height; + } + + public get nameBounds(): Rectangle { + return this.nameGraphic.getLocalBounds(); + } + + public get shortcutKeyBounds(): Rectangle { + if (this.shortcutKeyText) { + return this.shortcutKeyText.getLocalBounds(); + } else { + return new Rectangle(0, 0, 0, 0); + } + } + + public get style(): MenuCompletionItemStyle { + return this.menu.style.itemStyle; + } + + private checkPadding(padding: number | number[]) { + if (Array.isArray(padding)) { + if (padding.length !== 2 && padding.length !== 4) { + throw new Error('错误的padding数据'); + } + } + } + + private toWholePadding(padding: number | number[]): number[] { + this.checkPadding(padding); + if (Array.isArray(padding)) { + if (padding.length == 2) { + return [padding[0], padding[1], padding[0], padding[1]]; + } else { + return padding; + } + } else { + return [padding, padding, padding, padding]; + } + } + + public get paddingTop(): number { + return this.toWholePadding(this.menu.style.itemStyle.padding)[0]; + } + public get paddingBottom(): number { + return this.toWholePadding(this.menu.style.itemStyle.padding)[2]; + } + public get paddingLeft(): number { + return this.toWholePadding(this.menu.style.itemStyle.padding)[3]; + } + public get paddingRight(): number { + return this.toWholePadding(this.menu.style.itemStyle.padding)[1]; + } + + public get hoverColor(): string { + return this.style.hoverColor; + } + + public get fontSize(): number { + return this.style.fontSize; + } + + public get fontColor(): string { + if (this.config.disabled) { + return this.style.disabledFontColor; + } else if (this.config.fontColor) { + return this.config.fontColor; + } + return this.style.fontColor; + } + + initNameText(): Text { + this.nameText = new Text(this.config.name, { + fontSize: this.fontSize, + fill: this.fontColor, + }); + this.addChild(this.nameText); + return this.nameText; + } + + initShortcutKeyText(): Text | undefined { + if (this.config.shortcutKeys && this.config.shortcutKeys.length > 0) { + this.shortcutKeyText = new Text(this.config.shortcutKeys.join('+'), { + fontSize: this.fontSize, + fill: this.fontColor, + }); + this.addChild(this.shortcutKeyText); + return this.shortcutKeyText; + } + return undefined; + } + + initSubMenu() { + if (this.config.subMenu && this.config.subMenu.length > 0) { + this.arrowText = new Text('>', { + fontSize: this.fontSize, + fill: this.fontColor, + }); + this.addChild(this.arrowText); + this.subMenu = new ContextMenu( + { + name: `${this.config.name}子菜单`, + groups: this.config.subMenu, + style: this.menu.style, + }, + this + ); + } + } + + updateBackground(width: number, height: number) { + this.box.clear(); + const box = this.box; + const style = this.menu.style; + if (!style.itemStyle.hoverColor) { + throw new Error('未设置菜单项的hoverColor'); + } + let hoverColor = style.itemStyle.hoverColor; + if (this.style && this.style.hoverColor) { + hoverColor = this.style.hoverColor; + } + box.beginFill(utils.string2hex(hoverColor)); + if (style.borderRoundRadius > 0) { + box.drawRoundedRect(0, 0, width, height, style.borderRoundRadius); + } else { + box.drawRect(0, 0, width, height); + } + box.endFill(); + box.alpha = 0; + } + + updateBox(width: number, height: number) { + this.removeAllListeners(); + this.updateBackground(width, height); + this.nameText?.position.set(this.paddingLeft, this.paddingTop); + if (this.shortcutKeyText) { + const skTextWidth = this.shortcutKeyBounds.width; + this.shortcutKeyText.position.set( + width - skTextWidth - this.paddingRight, + this.paddingTop + ); + } + if (this.arrowText) { + this.arrowText.position.set( + width - this.paddingRight - this.gutter, + this.paddingTop + ); + } + + // 事件监听 + this.interactive = true; + this.hitArea = new Rectangle(0, 0, width, height); + this.cursor = 'pointer'; + this.on('pointerover', () => { + this.active = true; + if (this.config.disabled) { + this.cursor = 'not-allowed'; + } else { + this.cursor = 'pointer'; + } + if (this.subMenu) { + const p = this.toGlobal(new Point(this.width)); + this.subMenu.open(p); + } + }); + this.on('pointerout', () => { + this.active = false; + }); + this.on('pointertap', () => { + if (this.config.disabled) { + // 禁用,不处理 + return; + } + if (this.config.onClick) { + this.config.onClick(); + } + if (!this.config.subMenu || this.config.subMenu.length === 0) { + this.active = false; + this.menu.active = false; + this.menu.rootMenu.close(); + } + }); + } +} diff --git a/src/jlgraphic/ui/Menu.ts b/src/jlgraphic/ui/Menu.ts new file mode 100644 index 0000000..3ffbbd5 --- /dev/null +++ b/src/jlgraphic/ui/Menu.ts @@ -0,0 +1,173 @@ +/** + * 菜单配置项 + */ +export interface MenuOptions { + /** + * 菜单名称,需唯一 + */ + name: string; + /** + * 菜单标题 + */ + title?: string; + /** + * 菜单分组 + */ + groups: MenuGroupOptions[]; + /** + * 菜单样式 + */ + style?: MenuStyleOptions; +} +/** + * 菜单分组 + */ +export interface MenuGroupOptions { + /** + * 菜单项 + */ + items: MenuItemOptions[]; +} + +export interface MenuCompletionOptions extends MenuOptions { + style: MenuCompletionStyleOptions; +} +/** + * 菜单样式配置项 + */ +export interface MenuStyleOptions { + /** + * 菜单标题样式 + */ + titleStyle?: MenuItemStyle; + /** + * 菜单背景色 + */ + backgroundColor?: string; + /** + * 菜单边框线宽度,默认1,0为无线框 + */ + borderWidth?: number; + /** + * 菜单边框颜色 + */ + borderColor?: string; + /** + * 包围框是否圆角,小于等于0为直角,圆角的半径 + */ + borderRoundRadius?: number; + /** + * 菜单项样式 + */ + itemStyle?: MenuItemStyle; +} + +export interface MenuCompletionStyleOptions extends MenuStyleOptions { + titleStyle: MenuItemStyle; + backgroundColor: string; + border: boolean; + borderWidth: number; + borderColor: string; + borderRoundRadius: number; + itemStyle: MenuCompletionItemStyle; +} + +export interface MenuItemOptions { + /** + * 名称 + */ + name: string; + /** + * 是否禁用,默认不禁用 + */ + disabled?: boolean; + /** + * 快捷键 + */ + shortcutKeys?: string[]; + /** + * 点击处理 + */ + onClick?: () => void; + // /** + // * 菜单项字体样式,覆盖MenuOptions中的通用样式 + // */ + // style?: MenuItemStyle; + fontColor?: string; + /** + * 子菜单 + */ + subMenu?: MenuGroupOptions[]; +} + +export interface MenuItemStyle { + /** + * 字体大小 + */ + fontSize: number; + /** + * 字体颜色 + */ + fontColor?: string; + // /** + // * 字体对齐 + // */ + // align?: TextStyleAlign; + /** + * hover颜色 + */ + hoverColor?: string; + /** + * 禁用下字体颜色 + */ + disabledFontColor?: string; + /** + * 内边距 + */ + padding: number[] | number; // 内边距 +} +export interface MenuCompletionItemStyle extends MenuItemStyle { + fontColor: string; + // align: TextStyleAlign; + hoverColor: string; + /** + * 禁用下字体颜色 + */ + disabledFontColor: string; +} + +/** + * 默认的白色样式 + */ +export const DefaultWhiteStyleOptions: MenuCompletionStyleOptions = { + titleStyle: { + fontSize: 16, + fontColor: '#000000', + padding: [5, 15], + }, + backgroundColor: '#ffffff', + border: true, + borderWidth: 1, + borderColor: '#4C4C4C', + /** + * 默认直角 + */ + borderRoundRadius: 5, + itemStyle: { + fontSize: 16, + fontColor: '#000000', + padding: [5, 25], + // align: 'left', + hoverColor: '#1E78DB', + disabledFontColor: '#9D9D9D', + }, +}; + +/** + * 默认的白色菜单配置 + */ +export const DefaultWhiteMenuOptions: MenuCompletionOptions = { + name: '', + style: DefaultWhiteStyleOptions, + groups: [], +}; diff --git a/src/jlgraphic/utils/GraphicUtils.ts b/src/jlgraphic/utils/GraphicUtils.ts new file mode 100644 index 0000000..021ff30 --- /dev/null +++ b/src/jlgraphic/utils/GraphicUtils.ts @@ -0,0 +1,475 @@ +import { + Container, + DisplayObject, + IPointData, + Matrix, + Point, + Rectangle, +} from 'pixi.js'; +import Vector2 from '../math/Vector2'; +import { floatEquals, isZero } from '../math'; + +/** + * 递归父节点执行逻辑 + * @param obj + * @param handler + */ +export function recursiveParents( + obj: DisplayObject, + handler: (parent: Container) => void +): void { + if (obj.parent) { + handler(obj.parent); + recursiveParents(obj.parent, handler); + } +} + +/** + * 递归父节点查询父节点对象 + * @param obj + * @param finder + * @returns + */ +export function recursiveFindParent( + obj: DisplayObject, + finder: (parent: Container) => boolean +): Container | null { + if (obj.parent) { + if (finder(obj.parent)) { + return obj.parent; + } else { + return recursiveFindParent(obj.parent, finder); + } + } + return null; +} + +/** + * 递归子节点执行逻辑 + * @param container + * @param handler + */ +export function recursiveChildren( + container: Container, + handler: (child: DisplayObject) => void +): void { + container.children.forEach((child) => { + handler(child); + if (child.children) { + recursiveChildren(child as Container, handler); + } + }); +} + +/** + * 递归子节点查询子节点对象 + */ +export function recursiveFindChild( + container: Container, + finder: (child: DisplayObject) => boolean +): DisplayObject | null { + for (let i = 0; i < container.children.length; i++) { + const child = container.children[i]; + if (finder(child)) { + return child; + } else if (child.children) { + return recursiveFindChild(child as Container, finder); + } + } + return null; +} + +export interface BezierPoints { + p1: IPointData; + p2: IPointData; + cp1: IPointData; + cp2: IPointData; +} + +export function convertToBezierPoints(points: IPointData[]): BezierPoints[] { + if (points.length < 4 && points.length % 3 !== 1) { + throw new Error(`bezierCurve 数据错误: ${points}`); + } + const bps: BezierPoints[] = []; + for (let i = 0; i < points.length - 3; i += 3) { + const p1 = new Point(points[i].x, points[i].y); + const p2 = new Point(points[i + 3].x, points[i + 3].y); + const cp1 = new Point(points[i + 1].x, points[i + 1].y); + const cp2 = new Point(points[i + 2].x, points[i + 2].y); + bps.push({ + p1, + p2, + cp1, + cp2, + }); + } + return bps; +} + +/** + * 计算矩形中点 + */ +export function getRectangleCenter(rectangle: Rectangle): Point { + return new Point( + rectangle.x + rectangle.width / 2, + rectangle.y + rectangle.height / 2 + ); +} + +/** + * 计算两个矩形中心对齐的坐标, PS: 计算的是较大包围框的中心 + * @param rect1 + * @param rect2 + * @returns + */ +export function getCenterOfTwoRectangle( + rect1: Rectangle, + rect2: Rectangle +): Point { + const x = Math.abs(rect1.width - rect2.width) / 2; + const y = Math.abs(rect1.height - rect2.height) / 2; + return new Point(x, y); +} + +/** + * 增量更新点坐标 + * @param p + * @param dx + * @param dy + */ +export function updatePoint

( + p: P, + dx: number, + dy: number +): P { + p.x += dx; + p.y += dy; + return p; +} +/** + * 序列化图形变换 + * @param obj + * @returns + */ +export function serializeTransform(obj: DisplayObject): number[] { + const position = obj.position; + const scale = obj.scale; + const angle = obj.angle; + const skew = obj.skew; + return [position.x, position.y, scale.x, scale.y, angle, skew.x, skew.y]; +} +/** + * 反序列化变换数据到图形对象 + * @param obj + * @param transform + */ +export function deserializeTransformInto( + obj: DisplayObject, + transform: number[] +): void { + if (transform.length === 7) { + obj.position.set(transform[0], transform[1]); + obj.scale.set(transform[2], transform[3]); + obj.angle = transform[4]; + obj.skew.set(transform[5], transform[6]); + } else if (transform.length > 0) { + console.warn('错误的变换数据', transform); + } +} + +/** + * 计算变换后的点坐标 + * @param point 坐标点 + * @param transform 变换矩阵 + * @returns + */ +export function calculateTransformedPoint( + point: IPointData, + transform: Matrix +): Point { + const { a, b, c, d, tx, ty } = transform; + const x = a * point.x + c * point.y + tx; + const y = b * point.x + d * point.y + ty; + return new Point(x, y); +} + +/** + * 将直线转换为多边形 + * @param p1 + * @param p2 + * @param thick + * @returns + */ +export function convertLineToPolygonPoints( + p1: IPointData, + p2: IPointData, + thick: number +): IPointData[] { + const angle = Math.atan2(p2.y - p1.y, p2.x - p1.x) - Math.PI / 2; + const half = thick / 2; + const cos = Math.cos(angle) * half; + const sin = Math.sin(angle) * half; + return [ + new Point(p1.x - cos, p1.y - sin), + new Point(p2.x - cos, p2.y - sin), + new Point(p2.x + cos, p2.y + sin), + new Point(p1.x + cos, p1.y + sin), + ]; +} + +/** + * 转换矩形为多边形点坐标 + * @param rect 矩形 + * @returns + */ +export function convertRectangleToPolygonPoints(rect: Rectangle): IPointData[] { + return [ + new Point(rect.x, rect.y), + new Point(rect.x + rect.width, rect.y), + new Point(rect.x + rect.width, rect.y + rect.height), + new Point(rect.x, rect.y + rect.height), + ]; +} + +/** + * 计算线段中点坐标 + * @param p1 + * @param p2 + * @returns + */ +export function calculateLineMidpoint(p1: IPointData, p2: IPointData): Point { + const x = (p1.x + p2.x) / 2; + const y = (p1.y + p2.y) / 2; + return new Point(x, y); +} + +/** + * 计算点到直线距离 + * @param p1 + * @param p2 + * @param p + */ +export function calculateDistanceFromPointToLine( + p1: IPointData, + p2: IPointData, + p: IPointData +): number { + // 求直线的一般方程参数ABC,直线的一般式方程AX+BY+C=0 + const A = p1.y - p2.y; + const B = p2.x - p1.x; + const C = p1.x * p2.y - p1.y * p2.x; + // 计算点到直线垂直距离: d = |Ax+By+C|/sqrt(A*A+B*B),其中x,y为点坐标 + const dl = Math.abs(A * p.x + B * p.y + C) / Math.sqrt(A * A + B * B); + return dl; +} + +/** + * 计算点到直线的垂足坐标 + * @param p + * @param p1 + * @param p2 + */ +export function calculateFootPointFromPointToLine( + p1: IPointData, + p2: IPointData, + p: IPointData +): Point { + if (p1.x == p2.x && p1.y == p2.y) { + throw new Error(`直线两坐标点相等:${p1}`); + } + const k = -( + ((p1.x - p.x) * (p2.x - p1.x) + (p1.y - p.y) * (p2.y - p1.y)) / + (Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)) + ); + if (isZero(k)) { + return new Point(p.x, p.y); + } + const xf = k * (p2.x - p1.x) + p1.x; + const yf = k * (p2.y - p1.y) + p1.y; + return new Point(xf, yf); +} + +/** + * 计算直线与圆的交点 + * 1用直线到圆心的距离和半径相比,判断是否和圆有交点; + * 2求出圆心在直线上面的垂点; + * 3算出直线的单位向量e; + * 4求出一侧交点(Intersection)到projectPoint的长度(sideLength); + * 5求出sideLength和这侧端点到projectPoint距离的比例(ratio); + * 6projectPoint +/- ratio * e = 两侧交点; + * @param p0 圆心坐标 + * @param radius 圆半径 + * @param p1 直线坐标1 + * @param p2 直线坐标2 + * @returns 交点坐标,可能2/1/0个 + */ +export function calculateIntersectionPointOfCircleAndLine( + p0: IPointData, + radius: number, + p1: IPointData, + p2: IPointData +): Point[] { + const distance = calculateDistanceFromPointToLine(p1, p2, p0); + if (distance <= radius) { + // 有交点 + // 计算垂点 + const pr = calculateFootPointFromPointToLine(p1, p2, p0); + if (floatEquals(distance, radius)) { + // 切线 + return [pr]; + } + const vpr = new Vector2([pr.x, pr.y]); + const vc = new Vector2([p0.x, p0.y]); + // 计算直线单位向量 + const v1 = new Vector2([p1.x, p1.y]); + const v2 = new Vector2([p2.x, p2.y]); + const ve = Vector2.direction(v2, v1); + const base = Math.sqrt( + Math.abs(radius * radius - Vector2.difference(vpr, vc).squaredLength()) + ); + const vl = ve.scale(base); + const ip1 = Vector2.sum(vpr, vl); + const ip2 = Vector2.difference(vpr, vl); + return [new Point(ip1.x, ip1.y), new Point(ip2.x, ip2.y)]; + } else { + // 无交点 + return []; + } +} + +/** + * 计算圆心与圆心外一点与圆的交点(取圆心到点的向量与圆的交点) + * @param p0 圆心坐标 + * @param radius 圆半径 + * @param p 点坐标 + * @returns + */ +export function calculateIntersectionPointOfCircleAndPoint( + p0: IPointData, + radius: number, + p: IPointData +): Point { + const points = calculateIntersectionPointOfCircleAndLine(p0, radius, p0, p); + const vc = new Vector2([p0.x, p0.y]); + const vp = new Vector2([p.x, p.y]); + const vecp = Vector2.direction(vp, vc); + for (let i = 0; i < points.length; i++) { + const ip = points[i]; + const ve = Vector2.direction(new Vector2([ip.x, ip.y]), vc); + if (ve.equals(vecp)) { + return ip; + } + } + throw new Error('计算圆心与圆心外一点与圆的交点逻辑错误'); +} + +/** + * 计算点基于点的镜像点坐标 + * @param bp 基准点 + * @param p 待镜像的点坐标 + * @param distance 镜像点到基准点的距离,默认为p到基准点的距离 + * @returns + */ +export function calculateMirrorPoint( + bp: IPointData, + p: IPointData, + distance?: number +): Point { + const vbp = Vector2.from(bp); + const vp = Vector2.from(p); + const direction = Vector2.direction(vbp, vp); + if (distance == undefined) { + distance = Vector2.distance(vbp, vp); + } + const vmp = Vector2.sum(vbp, direction.scale(distance)); + return new Point(vmp.x, vmp.y); +} + +/** + * 计算基于给定轴的给定点的镜像点坐标 + * @param pa 给定轴线的坐标 + * @param pb 给定轴线的坐标 + * @param p 待镜像点坐标 + * @param distance + * @returns + */ +export function calculateMirrorPointBasedOnAxis( + pa: IPointData, + pb: IPointData, + p: IPointData, + distance?: number +): Point { + const fp = calculateFootPointFromPointToLine(pa, pb, p); + if (fp.equals(p)) { + return fp; + } else { + return calculateMirrorPoint(fp, p, distance); + } +} + +/** + * 计算直线与水平夹角,角度按顺时针,从0开始 + * @param p1 + * @param p2 + * @returns 角度,范围[0, 360) + */ +export function angleToAxisx(p1: IPointData, p2: IPointData): number { + if (p1.x == p2.x && p1.y == p2.y) { + throw new Error('一个点无法计算角度'); + } + const dx = Math.abs(p1.x - p2.x); + const dy = Math.abs(p1.y - p2.y); + + if (p2.x == p1.x) { + if (p2.y > p1.y) { + return 90; + } else { + return 270; + } + } + if (p2.y == p1.y) { + if (p2.x > p1.x) { + return 0; + } else { + return 180; + } + } + const angle = (Math.atan2(dy, dx) * 180) / Math.PI; + if (p2.x > p1.x) { + if (p2.y > p1.y) { + return angle; + } else if (p2.y < p1.y) { + return 360 - angle; + } + } else if (p2.x < p1.x) { + if (p2.y > p1.y) { + return 180 - angle; + } else { + return 180 + angle; + } + } + return angle; +} + +/** + * 计算两线夹角,pc与pa,pb的夹角,顺时针为正,逆时针为负 + * @param pa 交点 + * @param pb 锚定 + * @param pc + * @returns 夹角, [-180, 180] + */ +export function angleOfIncludedAngle( + pa: IPointData, + pb: IPointData, + pc: IPointData +): number { + const abAngle = angleToAxisx(pa, pb); + const acAngle = angleToAxisx(pa, pc); + let angle = acAngle - abAngle; + if (angle < -180) { + angle = 360 + angle; + } else if (angle > 180) { + angle = -(360 - angle); + } + return angle; +} diff --git a/src/jlgraphic/utils/IntersectUtils.ts b/src/jlgraphic/utils/IntersectUtils.ts new file mode 100644 index 0000000..2217a28 --- /dev/null +++ b/src/jlgraphic/utils/IntersectUtils.ts @@ -0,0 +1,362 @@ +import { IPointData, Point, Rectangle } from 'pixi.js'; +// /** +// * 点线碰撞检测 +// * @param pa 线段a端坐标 +// * @param pb 线段b端坐标 +// * @param p 点坐标 +// * @param tolerance 容忍度,越大检测范围越大 +// * @returns +// */ +// export function linePoint(pa: Point, pb: Point, p: Point, tolerance: number): boolean { +// return (Math.abs(distanceSquared(pa.x, pa.y, pb.x, pb.y) - (distanceSquared(pa.x, pa.y, p.x, p.y) + distanceSquared(pb.x, pb.y, p.x, p.y))) <= tolerance) +// } + +/** + * 根据点到直线的垂直距离计算碰撞 + * @param pa 线段a端坐标 + * @param pb 线段b端坐标 + * @param p 点坐标 + * @param lineWidth 线宽 + * @param exact 是否精确(使用给定线宽,否则线宽会设置为8) + * @returns + */ +export function linePoint( + pa: IPointData, + pb: IPointData, + p: IPointData, + lineWidth: number, + exact = false +): boolean { + if (!exact && lineWidth < 6) { + lineWidth = 6; + } + // 求直线的一般方程参数ABC,直线的一般式方程AX+BY+C=0 + const A = pa.y - pb.y; + const B = pb.x - pa.x; + const C = pa.x * pb.y - pa.y * pb.x; + // 计算点到直线垂直距离: d = |Ax+By+C|/sqrt(A*A+B*B),其中x,y为点坐标 + const dl = Math.abs(A * p.x + B * p.y + C) / Math.sqrt(A * A + B * B); + const intersect = dl <= lineWidth / 2; + if (intersect) { + // 距离在线宽范围内,再判断点是否超过线段两端点范围外(两端点外会有一点误差,两端点线宽一半半径的圆范围内) + const da = distance(pa.x, pa.y, p.x, p.y); + const db = distance(pb.x, pb.y, p.x, p.y); + const dab = distance(pa.x, pa.y, pb.x, pb.y); + return da <= dl + dab && db <= dl + dab; + } + return false; +} + +/** + * 折线与点碰撞 + * @param points 折线端点列表 + * @param p 点座标 + * @param lineWidth 线宽 + */ +export function polylinePoint( + points: IPointData[], + p: IPointData, + lineWidth: number +) { + const len = points.length; + for (let i = 0; i < len - 1; i++) { + if (linePoint(points[i], points[i + 1], p, lineWidth)) { + return true; + } + } + return false; +} + +/** + * 线线碰撞检测 + * @param pa 线段1a端坐标 + * @param pb 线段1b端坐标 + * @param p1 线段2a端坐标 + * @param p2 线段2b端坐标 + * @returns + */ +export function lineLine( + pa: IPointData, + pb: IPointData, + p1: IPointData, + p2: IPointData +): boolean { + const x1 = pa.x; + const y1 = pa.y; + const x2 = pb.x; + const y2 = pb.y; + const x3 = p1.x; + const y3 = p1.y; + const x4 = p2.x; + const y4 = p2.y; + const s1_x = x2 - x1; + const s1_y = y2 - y1; + const s2_x = x4 - x3; + const s2_y = y4 - y3; + const s = + (-s1_y * (x1 - x3) + s1_x * (y1 - y3)) / (-s2_x * s1_y + s1_x * s2_y); + const t = + (s2_x * (y1 - y3) - s2_y * (x1 - x3)) / (-s2_x * s1_y + s1_x * s2_y); + return s >= 0 && s <= 1 && t >= 0 && t <= 1; +} +/** + * 点和矩形碰撞检测 + * @param p 点作弊 + * @param rect 矩形 + * @returns + */ +export function pointBox(p: IPointData, rect: Rectangle): boolean { + const { x, y, width, height } = rect; + const x2 = p.x; + const y2 = p.y; + return x2 >= x && x2 <= x + width && y2 >= y && y2 <= y + height; +} +/** + * 线和矩形碰撞检测 + * @param pa 线段a端坐标 + * @param pb 线段b端坐标 + * @param rect 矩形 + * @returns + */ +export function lineBox( + pa: IPointData, + pb: IPointData, + rect: Rectangle +): boolean { + if (pointBox(pa, rect) || pointBox(pb, rect)) { + return true; + } + const { x, y, width, height } = rect; + const rp1 = new Point(x, y); + const rp2 = new Point(x + width, y); + const rp3 = new Point(x + width, y + height); + const rp4 = new Point(x, y + height); + return ( + lineLine(pa, pb, rp1, rp2) || + lineLine(pa, pb, rp2, rp3) || + lineLine(pa, pb, rp3, rp4) || + lineLine(pa, pb, rp1, rp4) + ); +} + +/** + * 多线段和矩形碰撞检测 + * @param points + * @param rect + * @returns false / 碰撞的线段序号 + */ +export function polylineBox(points: IPointData[], rect: Rectangle): boolean { + if (points.length < 2) { + return false; + } + + for (let i = 0; i < points.length - 1; i++) { + const p1 = points[i]; + const p2 = points[i + 1]; + + if (lineBox(p1, p2, rect)) { + return true; + } + } + return false; +} + +/** + * 两点碰撞检测 + * @param p1 + * @param p2 + * @param tolerance + * @returns + */ +export function pointPoint2( + p1: IPointData, + p2: IPointData, + tolerance: number +): boolean { + return pointPoint(p1.x, p1.y, p2.x, p2.y, tolerance); +} + +/** + * 两点碰撞检测 + * @param x1 + * @param y1 + * @param x2 + * @param y2 + * @param tolerance 容忍度/两点半径和 + * @returns + */ +export function pointPoint( + x1: number, + y1: number, + x2: number, + y2: number, + tolerance: number +): boolean { + return distance(x1, y1, x2, y2) <= tolerance; +} + +/** + * 两点距离 + * @param x1 + * @param y1 + * @param x2 + * @param y2 + * @returns + */ +export function distance(x1: number, y1: number, x2: number, y2: number) { + return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)); +} + +/** + * 两点距离 + * @param p1 + * @param p2 + * @returns + */ +export function distance2(p1: IPointData, p2: IPointData): number { + return distance(p1.x, p1.y, p2.x, p2.y); +} + +/** + * 圆和点的碰撞检测 + * @param x1 圆心x + * @param y1 圆心y + * @param r1 圆半径 + * @param x2 点x + * @param y2 点y + * @returns + */ +export function circlePoint( + x1: number, + y1: number, + r1: number, + x2: number, + y2: number +) { + const x = x2 - x1; + const y = y2 - y1; + return x * x + y * y <= r1 * r1; +} + +/** + * 圆和点的碰撞检测--不包括圆内部 + */ +export function circlePoint2( + x1: number, + y1: number, + r1: number, + x2: number, + y2: number, + tolerance: number +) { + const x = x2 - x1; + const y = y2 - y1; + return ( + x * x + y * y <= (r1 + tolerance) * (r1 + tolerance) && + x * x + y * y >= (r1 - tolerance) * (r1 - tolerance) + ); +} + +/** + * 点和多边形碰撞检测 + */ +export function pointPolygon( + p: IPointData, + points: IPointData[], + lineWidth: number +): boolean { + const { x, y } = p; + const length = points.length; + let c = false; + let i, j; + for (i = 0, j = length - 1; i < length; i++) { + if ( + points[i].y > y !== points[j].y > y && + x < + ((points[j].x - points[i].x) * (y - points[i].y)) / + (points[j].y - points[i].y) + + points[i].x + ) { + c = !c; + } + j = i; + } + if (c) { + return true; + } + for (i = 0; i < length - 1; i++) { + let p1, p2; + if (i === length - 1) { + p1 = points[i]; + p2 = points[0]; + } else { + p1 = points[i]; + p2 = points[i + 1]; + } + if (linePoint(p1, p2, p, lineWidth)) { + return true; + } + } + return false; +} + +/** + * 线和多边形碰撞检测 + * @param p1 + * @param p2 + * @param points + * @param tolerance 多边形包围线宽 + * @returns + */ +export function linePolygon( + p1: IPointData, + p2: IPointData, + points: IPointData[], + lineWidth: number, + polygonWidth: number +): boolean { + if (pointPolygon(p1, points, polygonWidth)) { + return true; + } + const length = points.length; + for (let i = 0; i < length; i++) { + let pa, pb; + if (i === length - 1) { + pa = points[i]; + pb = points[0]; + } else { + pa = points[i]; + pb = points[i + 1]; + } + // TODO:此处后续需考虑有线宽的情况 + if (lineLine(pa, pb, p1, p2)) { + return true; + } + } + return false; +} + +/** + * 多边线与多边形碰撞检测 + * @param polylinePoints 多边线所有点坐标 + * @param polygonPoints 多边形所有点坐标 + * @param polylineWidth 多边线的线宽 + * @param polygonWidth 多边形线宽 + * @returns + */ +export function polylinePolygon( + polylinePoints: IPointData[], + polygonPoints: IPointData[], + polylineWidth: number, + polygonWidth: number +): boolean { + const length = polylinePoints.length; + for (let i = 0; i < length - 1; i++) { + const p1 = polylinePoints[i]; + const p2 = polylinePoints[i + 1]; + if (linePolygon(p1, p2, polygonPoints, polylineWidth, polygonWidth)) { + return true; + } + } + return false; +} diff --git a/src/jlgraphic/utils/ObjectUtils.ts b/src/jlgraphic/utils/ObjectUtils.ts new file mode 100644 index 0000000..f95c19d --- /dev/null +++ b/src/jlgraphic/utils/ObjectUtils.ts @@ -0,0 +1,8 @@ +/** + * 判定是否代理对象 + * @param obj + * @returns + */ +export function isProxy(obj: any): boolean { + return !!(obj && obj['__Proxy']); +} diff --git a/src/jlgraphic/utils/index.ts b/src/jlgraphic/utils/index.ts new file mode 100644 index 0000000..ab051f1 --- /dev/null +++ b/src/jlgraphic/utils/index.ts @@ -0,0 +1,54 @@ +import { Point, Rectangle } from 'pixi.js'; + +export * from './GraphicUtils'; +export * from './IntersectUtils'; +export * from './ObjectUtils'; + +export const UP: Point = new Point(0, -1); +export const DOWN: Point = new Point(0, 1); +export const LEFT: Point = new Point(-1, 0); +export const RIGHT: Point = new Point(1, 0); + +/** + * 越界结果 + */ +export class OutOfBound { + left: boolean; + top: boolean; + right: boolean; + bottom: boolean; + constructor(left: boolean, top: boolean, right: boolean, bottom: boolean) { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + static check(rect: Rectangle, bound: Rectangle): OutOfBound { + const left = rect.left < bound.left; + const top = rect.top < bound.top; + const right = rect.right > bound.right; + const bottom = rect.bottom > bound.bottom; + return new OutOfBound(left, top, right, bottom); + } + static none(): OutOfBound { + return new OutOfBound(false, false, false, false); + } + static leftOut(): OutOfBound { + return new OutOfBound(true, false, false, false); + } + static topOut(): OutOfBound { + return new OutOfBound(false, true, false, false); + } + static rightOut(): OutOfBound { + return new OutOfBound(false, false, true, false); + } + static bottomOut(): OutOfBound { + return new OutOfBound(false, false, false, true); + } + static leftTopOut(): OutOfBound { + return new OutOfBound(true, true, false, false); + } + static rightBottomOut(): OutOfBound { + return new OutOfBound(false, false, true, true); + } +} diff --git a/src/layouts/DrawLayout.vue b/src/layouts/DrawLayout.vue new file mode 100644 index 0000000..9c7eda1 --- /dev/null +++ b/src/layouts/DrawLayout.vue @@ -0,0 +1,37 @@ + + + diff --git a/src/pages/ErrorNotFound.vue b/src/pages/ErrorNotFound.vue new file mode 100644 index 0000000..f4e0bb8 --- /dev/null +++ b/src/pages/ErrorNotFound.vue @@ -0,0 +1,27 @@ + + + diff --git a/src/pages/IndexPage.vue b/src/pages/IndexPage.vue new file mode 100644 index 0000000..1df5ee2 --- /dev/null +++ b/src/pages/IndexPage.vue @@ -0,0 +1,42 @@ + + + diff --git a/src/quasar.d.ts b/src/quasar.d.ts new file mode 100644 index 0000000..5937f7a --- /dev/null +++ b/src/quasar.d.ts @@ -0,0 +1,9 @@ +/* eslint-disable */ + +// Forces TS to apply `@quasar/app-vite` augmentations of `quasar` package +// Removing this would break `quasar/wrappers` imports as those typings are declared +// into `@quasar/app-vite` +// As a side effect, since `@quasar/app-vite` reference `quasar` to augment it, +// this declaration also apply `quasar` own +// augmentations (eg. adds `$q` into Vue component context) +/// diff --git a/src/router/index.ts b/src/router/index.ts new file mode 100644 index 0000000..4531114 --- /dev/null +++ b/src/router/index.ts @@ -0,0 +1,36 @@ +import { route } from 'quasar/wrappers'; +import { + createMemoryHistory, + createRouter, + createWebHashHistory, + createWebHistory, +} from 'vue-router'; + +import routes from './routes'; + +/* + * If not building with SSR mode, you can + * directly export the Router instantiation; + * + * The function below can be async too; either use + * async/await or return a Promise which resolves + * with the Router instance. + */ + +export default route(function (/* { store, ssrContext } */) { + const createHistory = process.env.SERVER + ? createMemoryHistory + : (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory); + + const Router = createRouter({ + scrollBehavior: () => ({ left: 0, top: 0 }), + routes, + + // Leave this as is and make changes in quasar.conf.js instead! + // quasar.conf.js -> build -> vueRouterMode + // quasar.conf.js -> build -> publicPath + history: createHistory(process.env.VUE_ROUTER_BASE), + }); + + return Router; +}); diff --git a/src/router/routes.ts b/src/router/routes.ts new file mode 100644 index 0000000..57f61e2 --- /dev/null +++ b/src/router/routes.ts @@ -0,0 +1,17 @@ +import { RouteRecordRaw } from 'vue-router'; + +const routes: RouteRecordRaw[] = [ + { + path: '/', + component: () => import('layouts/DrawLayout.vue'), + }, + + // Always leave this as last one, + // but you can also remove it + { + path: '/:catchAll(.*)*', + component: () => import('pages/ErrorNotFound.vue'), + }, +]; + +export default routes; diff --git a/src/shims-vue.d.ts b/src/shims-vue.d.ts new file mode 100644 index 0000000..4e6894b --- /dev/null +++ b/src/shims-vue.d.ts @@ -0,0 +1,10 @@ +/* eslint-disable */ + +/// + +// Mocks all files ending in `.vue` showing them as plain Vue instances +declare module '*.vue' { + import type { DefineComponent } from 'vue'; + const component: DefineComponent<{}, {}, any>; + export default component; +} diff --git a/src/stores/example-store.ts b/src/stores/example-store.ts new file mode 100644 index 0000000..83e8390 --- /dev/null +++ b/src/stores/example-store.ts @@ -0,0 +1,15 @@ +import { defineStore } from 'pinia'; + +export const useCounterStore = defineStore('counter', { + state: () => ({ + counter: 0, + }), + getters: { + doubleCount: (state) => state.counter * 2, + }, + actions: { + increment() { + this.counter++; + }, + }, +}); diff --git a/src/stores/index.ts b/src/stores/index.ts new file mode 100644 index 0000000..d30b7cf --- /dev/null +++ b/src/stores/index.ts @@ -0,0 +1,32 @@ +import { store } from 'quasar/wrappers' +import { createPinia } from 'pinia' +import { Router } from 'vue-router'; + +/* + * When adding new properties to stores, you should also + * extend the `PiniaCustomProperties` interface. + * @see https://pinia.vuejs.org/core-concepts/plugins.html#typing-new-store-properties + */ +declare module 'pinia' { + export interface PiniaCustomProperties { + readonly router: Router; + } +} + +/* + * If not building with SSR mode, you can + * directly export the Store instantiation; + * + * The function below can be async too; either use + * async/await or return a Promise which resolves + * with the Store instance. + */ + +export default store((/* { ssrContext } */) => { + const pinia = createPinia() + + // You can add Pinia plugins here + // pinia.use(SomePiniaPlugin) + + return pinia +}) diff --git a/src/stores/store-flag.d.ts b/src/stores/store-flag.d.ts new file mode 100644 index 0000000..7677175 --- /dev/null +++ b/src/stores/store-flag.d.ts @@ -0,0 +1,10 @@ +/* eslint-disable */ +// THIS FEATURE-FLAG FILE IS AUTOGENERATED, +// REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING +import "quasar/dist/types/feature-flag"; + +declare module "quasar/dist/types/feature-flag" { + interface QuasarFeatureFlags { + store: true; + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ee0d9cf --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "@quasar/app-vite/tsconfig-preset", + "compilerOptions": { + "baseUrl": "." + } +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..5dc1fc9 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,3348 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/parser@^7.16.4": + version "7.21.4" + resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" + integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== + +"@esbuild/linux-loong64@0.14.54": + version "0.14.54" + resolved "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028" + integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw== + +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.3.0": + version "4.4.0" + resolved "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0": + version "4.5.0" + resolved "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.5.0.tgz#f6f729b02feee2c749f57e334b7a1b5f40a81724" + integrity sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ== + +"@eslint/eslintrc@^2.0.2": + version "2.0.2" + resolved "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-2.0.2.tgz#01575e38707add677cf73ca1589abba8da899a02" + integrity sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.5.1" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.39.0": + version "8.39.0" + resolved "https://registry.npmmirror.com/@eslint/js/-/js-8.39.0.tgz#58b536bcc843f4cd1e02a7e6171da5c040f4d44b" + integrity sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng== + +"@humanwhocodes/config-array@^0.11.8": + version "0.11.8" + resolved "https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" + integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@pixi/accessibility@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/accessibility/-/accessibility-7.2.4.tgz#3198d0059c230c668b1179457346a3b5dcba6e64" + integrity sha512-EVjuqUqv9FeYFXCv0S0qj1hgCtbAMNBPCbOGEtiMogpM++/IySxBZvcOYg3rRgo9inwt2s4Bi7kUiqMPD8hItw== + +"@pixi/app@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/app/-/app-7.2.4.tgz#ae16fdc9fce04224fb36311168d902a2e7d0e65a" + integrity sha512-eJ2jpu5P28ip07nLItw6sETXn45P4KR/leMJ6zPHRlhT1m8t5zTsWr3jK4Uj8LF2E+6KlPNzLQh5Alf/unn/aQ== + +"@pixi/assets@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/assets/-/assets-7.2.4.tgz#944f4a15acc888071c0811d3d68524afb0ed069c" + integrity sha512-7199re3wvMAlVqXLaCyAr8IkJSXqkeVAxcYyB2rBu4Id5m2hhlGX1dQsdMBiCXLwu6/LLVqDvJggSNVQBzL6ZQ== + dependencies: + "@types/css-font-loading-module" "^0.0.7" + +"@pixi/color@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/color/-/color-7.2.4.tgz#6d6d5dbc01ae2a4f1c8eb48e98fff89ac0c3e40d" + integrity sha512-B/+9JRcXe2uE8wQfsueFRPZVayF2VEMRB7XGeRAsWCryOX19nmWhv0Nt3nOU2rvzI0niz9XgugJXsB6vVmDFSg== + dependencies: + colord "^2.9.3" + +"@pixi/compressed-textures@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/compressed-textures/-/compressed-textures-7.2.4.tgz#bbf84689a9f4f41d5a8e9476ea6520a4c19412ac" + integrity sha512-atnWyw/ot/Wg69qhgskKiuTYCZx15IxV35sa0KyXMthyjyvDLCIvOn0nczM6wCBy9H96SjJbfgynVWhVrip6qw== + +"@pixi/constants@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/constants/-/constants-7.2.4.tgz#45c23b247309e78d4105f04063ad8b453dae8b2f" + integrity sha512-hKuHBWR6N4Q0Sf5MGF3/9l+POg/G5rqhueHfzofiuelnKg7aBs3BVjjZ+6hZbd6M++vOUmxYelEX/NEFBxrheA== + +"@pixi/core@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/core/-/core-7.2.4.tgz#9f93a0744c795b17045127c2630f976580f03008" + integrity sha512-0XtvrfxHlS2T+beBBSpo7GI8+QLyyTqMVQpNmPqB4woYxzrOEJ9JaUFBaBfCvycLeUkfVih1u6HAbtF+2d1EjQ== + dependencies: + "@pixi/color" "7.2.4" + "@pixi/constants" "7.2.4" + "@pixi/extensions" "7.2.4" + "@pixi/math" "7.2.4" + "@pixi/runner" "7.2.4" + "@pixi/settings" "7.2.4" + "@pixi/ticker" "7.2.4" + "@pixi/utils" "7.2.4" + "@types/offscreencanvas" "^2019.6.4" + +"@pixi/display@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/display/-/display-7.2.4.tgz#cbf46ba0c0c0d30064b9ce67190a0a6a3624c62f" + integrity sha512-w5tqb8cWEO5qIDaO9GEqRvxYhL0iMk0Wsngw23bbLm1gLEQmrFkB2tpJlRAqd7H82C3DrDDeWvkrrxW6+m4apg== + +"@pixi/events@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/events/-/events-7.2.4.tgz#06434c9e84838b87d7626151ec556a66796ac206" + integrity sha512-/JtmoB98fzIU8giN9xvlRvmvOi6u4MaD2DnKNOMHkQ1MBraj3pmrXM9fZ0JbNzi+324GraAAY76QidgHjIYoYQ== + +"@pixi/extensions@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/extensions/-/extensions-7.2.4.tgz#ab2940abce3935706e956d1bcf2dbf44aca440db" + integrity sha512-Mnqv9scbL1ARD3QFKfOWs2aSVJJfP1dL8g5UiqGImYO3rZbz/9QCzXOeMVIZ5n3iaRyKMNhFFr84/zUja2H7Dw== + +"@pixi/extract@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/extract/-/extract-7.2.4.tgz#2db62611a3135ee8232affdb7b26cab37cb2a0a3" + integrity sha512-wlXZg+J2L/1jQhRi5nZQP/cXshovhjksjss91eAKMvY5aGxNAQovCP4xotJ/XJjfTvPMpeRzHPFYzm3PrOPQ7g== + +"@pixi/filter-alpha@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/filter-alpha/-/filter-alpha-7.2.4.tgz#f33621fa4bdc95de09457780aa33eb253fe6447f" + integrity sha512-UTUMSGyktUr+I9vmigqJo9iUhb0nwGyqTTME2xBWZvVGCnl5z+/wHxvIBBCe5pNZ66IM15pGXQ4cDcfqCuP2kA== + +"@pixi/filter-blur@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/filter-blur/-/filter-blur-7.2.4.tgz#834447f9d6edec7d27414c9961b9e6009acd678a" + integrity sha512-aLyXIoxy14bTansCPtbY8x7Sdn2OrrqkF/pcKiRXHJGGhi7wPacvB/NcmYJdnI/n2ExQ6V5Njuj/nfrsejVwcA== + +"@pixi/filter-color-matrix@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/filter-color-matrix/-/filter-color-matrix-7.2.4.tgz#4c9e6e174b27635ce5e92f34d372366b901e250f" + integrity sha512-DFtayybYXoUh73eHUFRK5REbi1t3FZuVUnaQTj+euHKF9L7EaYc3Q9wctpx1WPRcwkqEX50M4SNFhxpA7Pxtaw== + +"@pixi/filter-displacement@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/filter-displacement/-/filter-displacement-7.2.4.tgz#39da0592966079d7e194be46494b8055b5eebda2" + integrity sha512-Simq3IBJKt7+Gvk4kK7OFkfoeYUMhNhIyATCdeT+Jkdkq5WV7pYnH5hqO0YW7eAHrgjV13yn6t4H/GC4+6LhEA== + +"@pixi/filter-fxaa@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/filter-fxaa/-/filter-fxaa-7.2.4.tgz#78fac5466ca1a249f343be1af90c79bae399bf92" + integrity sha512-qzKjdL+Ih18uGTJLg8tT/H+YCsTeGkw2uF7lyKnw/lxGLJQhLWIhM95M9qSNgxbXyW1vp7SbG81a9aAEz2HAhA== + +"@pixi/filter-noise@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/filter-noise/-/filter-noise-7.2.4.tgz#0586a00381ec0e63f6c00d49cd58b781eaf07f37" + integrity sha512-QAU9Ybj2ZQrWM9ZEjTTC0iLnQcuyNoZNRinxSbg1G0yacpmsSb9wvV5ltIZ66+hfY+90+u2Nudt/v9g6pvOdGg== + +"@pixi/graphics@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/graphics/-/graphics-7.2.4.tgz#8500b604c36184736926393cb0ca9b9de9afef86" + integrity sha512-3A2EumTjWJgXlDLOyuBrl9b6v1Za/E+/IjOGUIX843HH4NYaf1a2sfDfljx6r3oiDvy+VhuBFmgynRcV5IyA0Q== + +"@pixi/math@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/math/-/math-7.2.4.tgz#219b64ca44348a1ee900ee074c51ee7e41615059" + integrity sha512-LJB+mozyEPllxa0EssFZrKNfVwysfaBun4b2dJKQQInp0DafgbA0j7A+WVg0oe51KhFULTJMpDqbLn/ITFc41A== + +"@pixi/mesh-extras@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/mesh-extras/-/mesh-extras-7.2.4.tgz#e3c6721c1a8ff5852e76402276b2f495b7db702d" + integrity sha512-Lxqq/1E2EmDgjZX8KzjhBy3VvITIQ00arr2ikyHYF1d0XtQTKEYpr8VKzhchqZ5/9DuyTDbDMYGhcxoNXQmZrQ== + +"@pixi/mesh@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/mesh/-/mesh-7.2.4.tgz#c78cc24f831a9e08d4ac0a1706e82f3498ba6907" + integrity sha512-wiALIqcRKib2BqeH9kOA5fOKWN352nqAspgbDa8gA7OyWzmNwqIedIlElixd0oLFOrIN5jOZAdzeKnoYQlt9Aw== + +"@pixi/mixin-cache-as-bitmap@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/mixin-cache-as-bitmap/-/mixin-cache-as-bitmap-7.2.4.tgz#4fb69efc40b30b0a8c2c1ad1eee6ca3227eccaed" + integrity sha512-95L/9nzfLHw6GoeqqRl/RjSloKvRt0xrc2inCmjMZvMsFUEtHN2F8IWd1k5vcv0S+83NCreFkJg6nJm1m5AZqg== + +"@pixi/mixin-get-child-by-name@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/mixin-get-child-by-name/-/mixin-get-child-by-name-7.2.4.tgz#863b14c774d3af7e2a38a68904c06bc51a2b51dd" + integrity sha512-9g17KgSBEEhkinnKk4dqmxagzHOCPSTvGB6lOopBq4yyXmr/2WVv+QGjuzE0O+p80szQeBJjPBQxzrfBILaSRw== + +"@pixi/mixin-get-global-position@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/mixin-get-global-position/-/mixin-get-global-position-7.2.4.tgz#8c0b96a0bcd381db9486954aeeb6d06c5ea2e2c0" + integrity sha512-UrAUF2BXCeWtFgR2m+er41Ky7zShT7r228cZkB6ZfYwMeThhwqG5mH68UeCyP6p68JMpT1gjI2DPfeSRY3ecnA== + +"@pixi/particle-container@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/particle-container/-/particle-container-7.2.4.tgz#8f277f65e73b061d0859c7e526f5161f9b090242" + integrity sha512-tpSzilZGFtAoi8XhzL0TecLPNRQAbY8nWV9XNGXJDw+nxXp18GCe8L6eEmnHLlAug67BRHl65DtrdvTknPX+4g== + +"@pixi/prepare@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/prepare/-/prepare-7.2.4.tgz#fd470bbc7dd90c4a8111989c405ffb5521850ff9" + integrity sha512-Yff5Sh4kTLdKc5VkkM44LW9gpj7Izw8ns3P1TzWxqeGjzPZ3folr/tQujGL+Qw+8A9VESp+hX9MSIHyw+jpyrg== + +"@pixi/runner@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/runner/-/runner-7.2.4.tgz#7356e768a43809ed6f8b3254e9bdd8c1a47af0e7" + integrity sha512-YtyqPk1LA+0guEFKSFx6t/YSvbEQwajFwi4Ft8iDhioa6VK2MmTir1GjWwy7JQYLcDmYSAcQjnmFtVTZohyYSw== + +"@pixi/settings@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/settings/-/settings-7.2.4.tgz#bfd3107ad425f99316018ee441accdf7d55627e6" + integrity sha512-ZPKRar9EwibijGmH8EViu4Greq1I/O7V/xQx2rNqN23XA7g09Qo6yfaeQpufu5xl8+/lZrjuHtQSnuY7OgG1CA== + dependencies: + "@pixi/constants" "7.2.4" + "@types/css-font-loading-module" "^0.0.7" + ismobilejs "^1.1.0" + +"@pixi/sprite-animated@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/sprite-animated/-/sprite-animated-7.2.4.tgz#46b95e52781dd7cf84ee315521c209e48c40656d" + integrity sha512-9eRriPSC0QVS7U9zQlrG3uEI5+h3fi+mqofXy+yjk1sGCmXSIJME5p2wg2mzxoJk3qkSMagQA9QHtL26Fti8Iw== + +"@pixi/sprite-tiling@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/sprite-tiling/-/sprite-tiling-7.2.4.tgz#7bcbd6e0096512fe18934a7b3250c57be19b63e4" + integrity sha512-nGfxQoACRx49dUN0oW1vFm3141M+7gkAbzoNJym2Pljd2dpLME9fb5E6Lyahu0yWMaPRhhGorn6z9VIGmTF3Jw== + +"@pixi/sprite@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/sprite/-/sprite-7.2.4.tgz#be7cd2d58d263131019545a83bb4df7340452ba1" + integrity sha512-DhR1B+/d0eXpxHIesJMXcVPrKFwQ+zRA1LvEIFfzewqfaRN3X6PMIuoKX8SIb6tl+Hq8Ba9Pe28zI7d2rmRzrA== + +"@pixi/spritesheet@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/spritesheet/-/spritesheet-7.2.4.tgz#9214d0c75aa95639c1f528091ac4a4850f5b5b8e" + integrity sha512-LNmlavyiMQeCF0U4S+yhzxUYmPmat6EpLjLnkGukQTZV5CZkxDCVgXM9uKoRF2DvNydj4yuwZ6+JjK8QssHI8Q== + +"@pixi/text-bitmap@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/text-bitmap/-/text-bitmap-7.2.4.tgz#444010da3898c35e2cdb01493bdc21706c9356a1" + integrity sha512-3u2CP4VN+muCaq/jtj7gn0hb3DET/X2S04zTBcgc2WVGufJc62yz+UDzS9jC+ellotVdt9c8U74++vpz3zJGfw== + +"@pixi/text-html@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/text-html/-/text-html-7.2.4.tgz#4702cdb97c6a10ca883d004808d45b1517c7129b" + integrity sha512-0NfLAE/w51ZtatxVqLvDS62iO0VLKsSdctqTAVv4Zlgdk9TKJmX1WUucHJboTvbm2SbDjNDGfZ6qXM5nAslIDQ== + +"@pixi/text@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/text/-/text-7.2.4.tgz#b31e7619ba80acee69cd9fb33948d34f1839bc61" + integrity sha512-DGu7ktpe+zHhqR2sG9NsJt4mgvSObv5EqXTtUxD4Z0li1gmqF7uktpLyn5I6vSg1TTEL4TECClRDClVDGiykWw== + +"@pixi/ticker@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/ticker/-/ticker-7.2.4.tgz#5acb761d3b53a1bbb2e34db59eb2a1b0442a8ed8" + integrity sha512-hQQHIHvGeFsP4GNezZqjzuhUgNQEVgCH9+qU05UX1Mc5UHC9l6OJnY4VTVhhcHxZjA6RnyaY+1zBxCnoXuazpg== + dependencies: + "@pixi/extensions" "7.2.4" + "@pixi/settings" "7.2.4" + "@pixi/utils" "7.2.4" + +"@pixi/utils@7.2.4": + version "7.2.4" + resolved "https://registry.npmmirror.com/@pixi/utils/-/utils-7.2.4.tgz#9f74e859481e3efbb6e54e524427b39a6d99829c" + integrity sha512-VUGQHBOINIS4ePzoqafwxaGPVRTa3oM/mEutIIHbNGI3b+QvSO+1Dnk40M0zcH6Bo+MxQZbOZK5X/wO9oU5+LQ== + dependencies: + "@pixi/color" "7.2.4" + "@pixi/constants" "7.2.4" + "@pixi/settings" "7.2.4" + "@types/earcut" "^2.1.0" + earcut "^2.2.4" + eventemitter3 "^4.0.0" + url "^0.11.0" + +"@positron/stack-trace@1.0.0": + version "1.0.0" + resolved "https://registry.npmmirror.com/@positron/stack-trace/-/stack-trace-1.0.0.tgz#14fcc712a530038ef9be1ce6952315a839f466a8" + integrity sha512-nWlGg+aMfQDhGYa5FtBhZwldeo2MtdjHdxmEQvhBXEnxgD5IhIYl0PHvex8SdwyN7qcSoMykMWdjyAX7ZxkpMw== + +"@quasar/app-vite@^1.0.0": + version "1.2.1" + resolved "https://registry.npmmirror.com/@quasar/app-vite/-/app-vite-1.2.1.tgz#23a634e16c74b28a38cf1ad1b3633388a220f97f" + integrity sha512-iKvpucrnt5pQuuQxqkLLWuI7Wm9Xx9fcBK1miMHWBgdcbHOXhc/OrlxeLvRwxm7GMd91cp3nhslo5j3a+zMCOg== + dependencies: + "@quasar/fastclick" "1.1.5" + "@quasar/vite-plugin" "^1.3.0" + "@rollup/pluginutils" "^4.1.2" + "@types/chrome" "^0.0.208" + "@types/compression" "^1.7.2" + "@types/cordova" "0.0.34" + "@types/express" "^4.17.13" + "@vitejs/plugin-vue" "^2.2.0" + archiver "^5.3.0" + chokidar "^3.5.3" + ci-info "^3.7.1" + compression "^1.7.4" + cross-spawn "^7.0.3" + dot-prop "6.0.1" + elementtree "0.1.7" + esbuild "0.14.51" + express "^4.17.3" + fast-glob "3.2.12" + fs-extra "^11.1.0" + html-minifier "^4.0.0" + inquirer "^8.2.1" + isbinaryfile "^5.0.0" + kolorist "^1.5.1" + lodash "^4.17.21" + minimist "^1.2.6" + open "^8.4.0" + ouch "^2.0.0" + register-service-worker "^1.7.2" + rollup-plugin-visualizer "^5.5.4" + sass "1.32.12" + semver "^7.3.5" + serialize-javascript "^6.0.0" + table "^6.8.0" + vite "^2.9.13" + webpack-merge "^5.8.0" + +"@quasar/extras@^1.0.0": + version "1.16.2" + resolved "https://registry.npmmirror.com/@quasar/extras/-/extras-1.16.2.tgz#fcc6374a882253051f9d7302cb9ba828f0010cdf" + integrity sha512-spDc1DrwxGts0MjmOAJ11xpxJANhCI1vEadxaw89wRQJ/QfKd0HZrwN7uN1U15cRozGRkJpdbsnP4cVXpkPysA== + +"@quasar/fastclick@1.1.5": + version "1.1.5" + resolved "https://registry.npmmirror.com/@quasar/fastclick/-/fastclick-1.1.5.tgz#948e79c44098cced6c3d1645315683ebc29ed834" + integrity sha512-p3JKgTjRlJ1YQXbqTw3Bsa4j0mQdt5dq+WfYvyb7MgKGdephHCKdR/kxA5PCTAmJanGJuDKqRdyGYX/hYN4KGw== + +"@quasar/vite-plugin@^1.3.0": + version "1.3.0" + resolved "https://registry.npmmirror.com/@quasar/vite-plugin/-/vite-plugin-1.3.0.tgz#c399b00d40c4a40aadb0e90a8e9116d72379d339" + integrity sha512-wd7b/QrKu/WB53l+vxo6GLbkoEwh5FFKLtLl3fM8TXrDDRB1qsKKkRa/vwTU4dpuyBaOZDjycmYuAsOlTdNp8w== + +"@rollup/pluginutils@^4.1.2": + version "4.2.1" + resolved "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d" + integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ== + dependencies: + estree-walker "^2.0.1" + picomatch "^2.2.2" + +"@stomp/stompjs@^7.0.0": + version "7.0.0" + resolved "https://registry.npmmirror.com/@stomp/stompjs/-/stompjs-7.0.0.tgz#46b5c454a9dc8262e0b20f3b3dbacaa113993077" + integrity sha512-fGdq4wPDnSV/KyOsjq4P+zLc8MFWC3lMmP5FBgLWKPJTYcuCbAIrnRGjB7q2jHZdYCOD5vxLuFoKIYLy5/u8Pw== + +"@types/body-parser@*": + version "1.19.2" + resolved "https://registry.npmmirror.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" + integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/chrome@^0.0.208": + version "0.0.208" + resolved "https://registry.npmmirror.com/@types/chrome/-/chrome-0.0.208.tgz#c52992e46723c783d3fd84a8b90dd8b3e87af67f" + integrity sha512-VDU/JnXkF5qaI7WBz14Azpa2VseZTgML0ia/g/B1sr9OfdOnHiH/zZ7P7qCDqxSlkqJh76/bPc8jLFcx8rHJmw== + dependencies: + "@types/filesystem" "*" + "@types/har-format" "*" + +"@types/compression@^1.7.2": + version "1.7.2" + resolved "https://registry.npmmirror.com/@types/compression/-/compression-1.7.2.tgz#7cc1cdb01b4730eea284615a68fc70a2cdfd5e71" + integrity sha512-lwEL4M/uAGWngWFLSG87ZDr2kLrbuR8p7X+QZB1OQlT+qkHsCPDVFnHPyXf4Vyl4yDDorNY+mAhosxkCvppatg== + dependencies: + "@types/express" "*" + +"@types/connect@*": + version "3.4.35" + resolved "https://registry.npmmirror.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + dependencies: + "@types/node" "*" + +"@types/cordova@0.0.34": + version "0.0.34" + resolved "https://registry.npmmirror.com/@types/cordova/-/cordova-0.0.34.tgz#ea7addf74ecec3d7629827a0c39e2c9addc73d04" + integrity sha512-rkiiTuf/z2wTd4RxFOb+clE7PF4AEJU0hsczbUdkHHBtkUmpWQpEddynNfJYKYtZFJKbq4F+brfekt1kx85IZA== + +"@types/css-font-loading-module@^0.0.7": + version "0.0.7" + resolved "https://registry.npmmirror.com/@types/css-font-loading-module/-/css-font-loading-module-0.0.7.tgz#2f98ede46acc0975de85c0b7b0ebe06041d24601" + integrity sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q== + +"@types/earcut@^2.1.0": + version "2.1.1" + resolved "https://registry.npmmirror.com/@types/earcut/-/earcut-2.1.1.tgz#573a0af609f17005c751f6f4ffec49cfe358ea51" + integrity sha512-w8oigUCDjElRHRRrMvn/spybSMyX8MTkKA5Dv+tS1IE/TgmNZPqUYtvYBXGY8cieSE66gm+szeK+bnbxC2xHTQ== + +"@types/express-serve-static-core@^4.17.33": + version "4.17.33" + resolved "https://registry.npmmirror.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz#de35d30a9d637dc1450ad18dd583d75d5733d543" + integrity sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@*", "@types/express@^4.17.13": + version "4.17.17" + resolved "https://registry.npmmirror.com/@types/express/-/express-4.17.17.tgz#01d5437f6ef9cfa8668e616e13c2f2ac9a491ae4" + integrity sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/filesystem@*": + version "0.0.32" + resolved "https://registry.npmmirror.com/@types/filesystem/-/filesystem-0.0.32.tgz#307df7cc084a2293c3c1a31151b178063e0a8edf" + integrity sha512-Yuf4jR5YYMR2DVgwuCiP11s0xuVRyPKmz8vo6HBY3CGdeMj8af93CFZX+T82+VD1+UqHOxTq31lO7MI7lepBtQ== + dependencies: + "@types/filewriter" "*" + +"@types/filewriter@*": + version "0.0.29" + resolved "https://registry.npmmirror.com/@types/filewriter/-/filewriter-0.0.29.tgz#a48795ecadf957f6c0d10e0c34af86c098fa5bee" + integrity sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ== + +"@types/google-protobuf@^3.15.6": + version "3.15.6" + resolved "https://registry.npmmirror.com/@types/google-protobuf/-/google-protobuf-3.15.6.tgz#674a69493ef2c849b95eafe69167ea59079eb504" + integrity sha512-pYVNNJ+winC4aek+lZp93sIKxnXt5qMkuKmaqS3WGuTq0Bw1ZDYNBgzG5kkdtwcv+GmYJGo3yEg6z2cKKAiEdw== + +"@types/har-format@*": + version "1.2.10" + resolved "https://registry.npmmirror.com/@types/har-format/-/har-format-1.2.10.tgz#7b4e1e0ada4d17684ac3b05d601a4871cfab11fc" + integrity sha512-o0J30wqycjF5miWDKYKKzzOU1ZTLuA42HZ4HE7/zqTOc/jTLdQ5NhYWvsRQo45Nfi1KHoRdNhteSI4BAxTF1Pg== + +"@types/json-schema@^7.0.9": + version "7.0.11" + resolved "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" + integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== + +"@types/mime@*": + version "3.0.1" + resolved "https://registry.npmmirror.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" + integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== + +"@types/node@*": + version "18.16.0" + resolved "https://registry.npmmirror.com/@types/node/-/node-18.16.0.tgz#4668bc392bb6938637b47e98b1f2ed5426f33316" + integrity sha512-BsAaKhB+7X+H4GnSjGhJG9Qi8Tw+inU9nJDwmD5CgOmBLEI6ArdhikpLX7DjbjDRDTbqZzU2LSQNZg8WGPiSZQ== + +"@types/node@^12.20.21": + version "12.20.55" + resolved "https://registry.npmmirror.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" + integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== + +"@types/offscreencanvas@^2019.6.4": + version "2019.7.0" + resolved "https://registry.npmmirror.com/@types/offscreencanvas/-/offscreencanvas-2019.7.0.tgz#e4a932069db47bb3eabeb0b305502d01586fa90d" + integrity sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg== + +"@types/qs@*": + version "6.9.7" + resolved "https://registry.npmmirror.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + +"@types/range-parser@*": + version "1.2.4" + resolved "https://registry.npmmirror.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + +"@types/semver@^7.3.12": + version "7.3.13" + resolved "https://registry.npmmirror.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" + integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== + +"@types/serve-static@*": + version "1.15.1" + resolved "https://registry.npmmirror.com/@types/serve-static/-/serve-static-1.15.1.tgz#86b1753f0be4f9a1bee68d459fcda5be4ea52b5d" + integrity sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ== + dependencies: + "@types/mime" "*" + "@types/node" "*" + +"@typescript-eslint/eslint-plugin@^5.10.0": + version "5.59.1" + resolved "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.1.tgz#9b09ee1541bff1d2cebdcb87e7ce4a4003acde08" + integrity sha512-AVi0uazY5quFB9hlp2Xv+ogpfpk77xzsgsIEWyVS7uK/c7MZ5tw7ZPbapa0SbfkqE0fsAMkz5UwtgMLVk2BQAg== + dependencies: + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.59.1" + "@typescript-eslint/type-utils" "5.59.1" + "@typescript-eslint/utils" "5.59.1" + debug "^4.3.4" + grapheme-splitter "^1.0.4" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/parser@^5.10.0": + version "5.59.1" + resolved "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-5.59.1.tgz#73c2c12127c5c1182d2e5b71a8fa2a85d215cbb4" + integrity sha512-nzjFAN8WEu6yPRDizIFyzAfgK7nybPodMNFGNH0M9tei2gYnYszRDqVA0xlnRjkl7Hkx2vYrEdb6fP2a21cG1g== + dependencies: + "@typescript-eslint/scope-manager" "5.59.1" + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/typescript-estree" "5.59.1" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.59.1": + version "5.59.1" + resolved "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.1.tgz#8a20222719cebc5198618a5d44113705b51fd7fe" + integrity sha512-mau0waO5frJctPuAzcxiNWqJR5Z8V0190FTSqRw1Q4Euop6+zTwHAf8YIXNwDOT29tyUDrQ65jSg9aTU/H0omA== + dependencies: + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/visitor-keys" "5.59.1" + +"@typescript-eslint/type-utils@5.59.1": + version "5.59.1" + resolved "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-5.59.1.tgz#63981d61684fd24eda2f9f08c0a47ecb000a2111" + integrity sha512-ZMWQ+Oh82jWqWzvM3xU+9y5U7MEMVv6GLioM3R5NJk6uvP47kZ7YvlgSHJ7ERD6bOY7Q4uxWm25c76HKEwIjZw== + dependencies: + "@typescript-eslint/typescript-estree" "5.59.1" + "@typescript-eslint/utils" "5.59.1" + debug "^4.3.4" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.59.1": + version "5.59.1" + resolved "https://registry.npmmirror.com/@typescript-eslint/types/-/types-5.59.1.tgz#03f3fedd1c044cb336ebc34cc7855f121991f41d" + integrity sha512-dg0ICB+RZwHlysIy/Dh1SP+gnXNzwd/KS0JprD3Lmgmdq+dJAJnUPe1gNG34p0U19HvRlGX733d/KqscrGC1Pg== + +"@typescript-eslint/typescript-estree@5.59.1": + version "5.59.1" + resolved "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.1.tgz#4aa546d27fd0d477c618f0ca00b483f0ec84c43c" + integrity sha512-lYLBBOCsFltFy7XVqzX0Ju+Lh3WPIAWxYpmH/Q7ZoqzbscLiCW00LeYCdsUnnfnj29/s1WovXKh2gwCoinHNGA== + dependencies: + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/visitor-keys" "5.59.1" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.59.1": + version "5.59.1" + resolved "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-5.59.1.tgz#d89fc758ad23d2157cfae53f0b429bdf15db9473" + integrity sha512-MkTe7FE+K1/GxZkP5gRj3rCztg45bEhsd8HYjczBuYm+qFHP5vtZmjx3B0yUCDotceQ4sHgTyz60Ycl225njmA== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.59.1" + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/typescript-estree" "5.59.1" + eslint-scope "^5.1.1" + semver "^7.3.7" + +"@typescript-eslint/visitor-keys@5.59.1": + version "5.59.1" + resolved "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.1.tgz#0d96c36efb6560d7fb8eb85de10442c10d8f6058" + integrity sha512-6waEYwBTCWryx0VJmP7JaM4FpipLsFl9CvYf2foAE8Qh/Y0s+bxWysciwOs0LTBED4JCaNxTZ5rGadB14M6dwA== + dependencies: + "@typescript-eslint/types" "5.59.1" + eslint-visitor-keys "^3.3.0" + +"@vitejs/plugin-vue@^2.2.0": + version "2.3.4" + resolved "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-2.3.4.tgz#966a6279060eb2d9d1a02ea1a331af071afdcf9e" + integrity sha512-IfFNbtkbIm36O9KB8QodlwwYvTEsJb4Lll4c2IwB3VHc2gie2mSPtSzL0eYay7X2jd/2WX02FjSGTWR6OPr/zg== + +"@vue/compiler-core@3.2.47": + version "3.2.47" + resolved "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.47.tgz#3e07c684d74897ac9aa5922c520741f3029267f8" + integrity sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig== + dependencies: + "@babel/parser" "^7.16.4" + "@vue/shared" "3.2.47" + estree-walker "^2.0.2" + source-map "^0.6.1" + +"@vue/compiler-dom@3.2.47": + version "3.2.47" + resolved "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz#a0b06caf7ef7056939e563dcaa9cbde30794f305" + integrity sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ== + dependencies: + "@vue/compiler-core" "3.2.47" + "@vue/shared" "3.2.47" + +"@vue/compiler-sfc@3.2.47": + version "3.2.47" + resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz#1bdc36f6cdc1643f72e2c397eb1a398f5004ad3d" + integrity sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ== + dependencies: + "@babel/parser" "^7.16.4" + "@vue/compiler-core" "3.2.47" + "@vue/compiler-dom" "3.2.47" + "@vue/compiler-ssr" "3.2.47" + "@vue/reactivity-transform" "3.2.47" + "@vue/shared" "3.2.47" + estree-walker "^2.0.2" + magic-string "^0.25.7" + postcss "^8.1.10" + source-map "^0.6.1" + +"@vue/compiler-ssr@3.2.47": + version "3.2.47" + resolved "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz#35872c01a273aac4d6070ab9d8da918ab13057ee" + integrity sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw== + dependencies: + "@vue/compiler-dom" "3.2.47" + "@vue/shared" "3.2.47" + +"@vue/devtools-api@^6.4.5", "@vue/devtools-api@^6.5.0": + version "6.5.0" + resolved "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.5.0.tgz#98b99425edee70b4c992692628fa1ea2c1e57d07" + integrity sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q== + +"@vue/reactivity-transform@3.2.47": + version "3.2.47" + resolved "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz#e45df4d06370f8abf29081a16afd25cffba6d84e" + integrity sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA== + dependencies: + "@babel/parser" "^7.16.4" + "@vue/compiler-core" "3.2.47" + "@vue/shared" "3.2.47" + estree-walker "^2.0.2" + magic-string "^0.25.7" + +"@vue/reactivity@3.2.47": + version "3.2.47" + resolved "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.47.tgz#1d6399074eadfc3ed35c727e2fd707d6881140b6" + integrity sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ== + dependencies: + "@vue/shared" "3.2.47" + +"@vue/runtime-core@3.2.47": + version "3.2.47" + resolved "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.2.47.tgz#406ebade3d5551c00fc6409bbc1eeb10f32e121d" + integrity sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA== + dependencies: + "@vue/reactivity" "3.2.47" + "@vue/shared" "3.2.47" + +"@vue/runtime-dom@3.2.47": + version "3.2.47" + resolved "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz#93e760eeaeab84dedfb7c3eaf3ed58d776299382" + integrity sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA== + dependencies: + "@vue/runtime-core" "3.2.47" + "@vue/shared" "3.2.47" + csstype "^2.6.8" + +"@vue/server-renderer@3.2.47": + version "3.2.47" + resolved "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.2.47.tgz#8aa1d1871fc4eb5a7851aa7f741f8f700e6de3c0" + integrity sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA== + dependencies: + "@vue/compiler-ssr" "3.2.47" + "@vue/shared" "3.2.47" + +"@vue/shared@3.2.47": + version "3.2.47" + resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.47.tgz#e597ef75086c6e896ff5478a6bfc0a7aa4bbd14c" + integrity sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ== + +accepts@~1.3.5, accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.npmmirror.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.8.0: + version "8.8.2" + resolved "https://registry.npmmirror.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + +ajv@^6.10.0, ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.0.1: + version "8.12.0" + resolved "https://registry.npmmirror.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +archiver-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2" + integrity sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw== + dependencies: + glob "^7.1.4" + graceful-fs "^4.2.0" + lazystream "^1.0.0" + lodash.defaults "^4.2.0" + lodash.difference "^4.5.0" + lodash.flatten "^4.4.0" + lodash.isplainobject "^4.0.6" + lodash.union "^4.6.0" + normalize-path "^3.0.0" + readable-stream "^2.0.0" + +archiver@^5.3.0: + version "5.3.1" + resolved "https://registry.npmmirror.com/archiver/-/archiver-5.3.1.tgz#21e92811d6f09ecfce649fbefefe8c79e57cbbb6" + integrity sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w== + dependencies: + archiver-utils "^2.1.0" + async "^3.2.3" + buffer-crc32 "^0.2.1" + readable-stream "^3.6.0" + readdir-glob "^1.0.0" + tar-stream "^2.2.0" + zip-stream "^4.1.0" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.npmmirror.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + +async@^3.2.3: + version "3.2.4" + resolved "https://registry.npmmirror.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== + +autoprefixer@^10.4.2: + version "10.4.14" + resolved "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.14.tgz#e28d49902f8e759dd25b153264e862df2705f79d" + integrity sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ== + dependencies: + browserslist "^4.21.5" + caniuse-lite "^1.0.30001464" + fraction.js "^4.2.0" + normalize-range "^0.1.2" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +bl@^4.0.3, bl@^4.1.0: + version "4.1.0" + resolved "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +body-parser@1.20.1: + version "1.20.1" + resolved "https://registry.npmmirror.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browserslist@^4.21.5: + version "4.21.5" + resolved "https://registry.npmmirror.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" + integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== + dependencies: + caniuse-lite "^1.0.30001449" + electron-to-chromium "^1.4.284" + node-releases "^2.0.8" + update-browserslist-db "^1.0.10" + +buffer-crc32@^0.2.1, buffer-crc32@^0.2.13: + version "0.2.13" + resolved "https://registry.npmmirror.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind@^1.0.0: + version "1.0.2" + resolved "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camel-case@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" + integrity sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w== + dependencies: + no-case "^2.2.0" + upper-case "^1.1.1" + +caniuse-lite@^1.0.30001449, caniuse-lite@^1.0.30001464: + version "1.0.30001481" + resolved "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001481.tgz#f58a717afe92f9e69d0e35ff64df596bfad93912" + integrity sha512-KCqHwRnaa1InZBtqXzP98LPg0ajCVujMKjqKDhZEthIpAsJl/YEIa3YvXjGXPVqzZVguccuu7ga9KOE1J9rKPQ== + +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1: + version "4.1.2" + resolved "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.npmmirror.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.npmmirror.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +ci-info@^3.7.1: + version "3.8.0" + resolved "https://registry.npmmirror.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" + integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== + +clean-css@^4.2.1: + version "4.2.4" + resolved "https://registry.npmmirror.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178" + integrity sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A== + dependencies: + source-map "~0.6.0" + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-spinners@^2.5.0: + version "2.8.0" + resolved "https://registry.npmmirror.com/cli-spinners/-/cli-spinners-2.8.0.tgz#e97a3e2bd00e6d85aa0c13d7f9e3ce236f7787fc" + integrity sha512-/eG5sJcvEIwxcdYM86k5tPwn0MUzkX5YY3eImTGpJOZgVe4SdTMY14vQpcxgBzJ0wXwAYrS8E+c3uHeK4JNyzQ== + +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.npmmirror.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.npmmirror.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colord@^2.9.3: + version "2.9.3" + resolved "https://registry.npmmirror.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" + integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== + +commander@^2.19.0: + version "2.20.3" + resolved "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +compress-commons@^4.1.0: + version "4.1.1" + resolved "https://registry.npmmirror.com/compress-commons/-/compress-commons-4.1.1.tgz#df2a09a7ed17447642bad10a85cc9a19e5c42a7d" + integrity sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ== + dependencies: + buffer-crc32 "^0.2.13" + crc32-stream "^4.0.2" + normalize-path "^3.0.0" + readable-stream "^3.6.0" + +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.npmmirror.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.npmmirror.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.npmmirror.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4: + version "1.0.5" + resolved "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.npmmirror.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +crc-32@^1.2.0: + version "1.2.2" + resolved "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" + integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== + +crc32-stream@^4.0.2: + version "4.0.2" + resolved "https://registry.npmmirror.com/crc32-stream/-/crc32-stream-4.0.2.tgz#c922ad22b38395abe9d3870f02fa8134ed709007" + integrity sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w== + dependencies: + crc-32 "^1.2.0" + readable-stream "^3.4.0" + +cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +csstype@^2.6.8: + version "2.6.21" + resolved "https://registry.npmmirror.com/csstype/-/csstype-2.6.21.tgz#2efb85b7cc55c80017c66a5ad7cbd931fda3a90e" + integrity sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w== + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +defaults@^1.0.3: + version "1.0.4" + resolved "https://registry.npmmirror.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" + integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== + dependencies: + clone "^1.0.2" + +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dot-prop@6.0.1: + version "6.0.1" + resolved "https://registry.npmmirror.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" + integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA== + dependencies: + is-obj "^2.0.0" + +earcut@^2.2.4: + version "2.2.4" + resolved "https://registry.npmmirror.com/earcut/-/earcut-2.2.4.tgz#6d02fd4d68160c114825d06890a92ecaae60343a" + integrity sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ== + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +ejs@^3.1.7: + version "3.1.9" + resolved "https://registry.npmmirror.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361" + integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ== + dependencies: + jake "^10.8.5" + +electron-to-chromium@^1.4.284: + version "1.4.371" + resolved "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.371.tgz#393983ef087268a20c926a89be30e9f0bfc803b0" + integrity sha512-jlBzY4tFcJaiUjzhRTCWAqRvTO/fWzjA3Bls0mykzGZ7zvcMP7h05W6UcgzfT9Ca1SW2xyKDOFRyI0pQeRNZGw== + +elementtree@0.1.7: + version "0.1.7" + resolved "https://registry.npmmirror.com/elementtree/-/elementtree-0.1.7.tgz#9ac91be6e52fb6e6244c4e54a4ac3ed8ae8e29c0" + integrity sha512-wkgGT6kugeQk/P6VZ/f4T+4HB41BVgNBq5CDIZVbQ02nvTVqAiVTbskxxu3eA/X96lMlfYOwnLQpN2v5E1zDEg== + dependencies: + sax "1.1.4" + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +esbuild-android-64@0.14.51: + version "0.14.51" + resolved "https://registry.npmmirror.com/esbuild-android-64/-/esbuild-android-64-0.14.51.tgz#414a087cb0de8db1e347ecca6c8320513de433db" + integrity sha512-6FOuKTHnC86dtrKDmdSj2CkcKF8PnqkaIXqvgydqfJmqBazCPdw+relrMlhGjkvVdiiGV70rpdnyFmA65ekBCQ== + +esbuild-android-64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be" + integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ== + +esbuild-android-arm64@0.14.51: + version "0.14.51" + resolved "https://registry.npmmirror.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.51.tgz#55de3bce2aab72bcd2b606da4318ad00fb9c8151" + integrity sha512-vBtp//5VVkZWmYYvHsqBRCMMi1MzKuMIn5XDScmnykMTu9+TD9v0NMEDqQxvtFToeYmojdo5UCV2vzMQWJcJ4A== + +esbuild-android-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz#8ce69d7caba49646e009968fe5754a21a9871771" + integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg== + +esbuild-darwin-64@0.14.51: + version "0.14.51" + resolved "https://registry.npmmirror.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.51.tgz#4259f23ed6b4cea2ec8a28d87b7fb9801f093754" + integrity sha512-YFmXPIOvuagDcwCejMRtCDjgPfnDu+bNeh5FU2Ryi68ADDVlWEpbtpAbrtf/lvFTWPexbgyKgzppNgsmLPr8PA== + +esbuild-darwin-64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz#24ba67b9a8cb890a3c08d9018f887cc221cdda25" + integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug== + +esbuild-darwin-arm64@0.14.51: + version "0.14.51" + resolved "https://registry.npmmirror.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.51.tgz#d77b4366a71d84e530ba019d540b538b295d494a" + integrity sha512-juYD0QnSKwAMfzwKdIF6YbueXzS6N7y4GXPDeDkApz/1RzlT42mvX9jgNmyOlWKN7YzQAYbcUEJmZJYQGdf2ow== + +esbuild-darwin-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz#3f7cdb78888ee05e488d250a2bdaab1fa671bf73" + integrity sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw== + +esbuild-freebsd-64@0.14.51: + version "0.14.51" + resolved "https://registry.npmmirror.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.51.tgz#27b6587b3639f10519c65e07219d249b01f2ad38" + integrity sha512-cLEI/aXjb6vo5O2Y8rvVSQ7smgLldwYY5xMxqh/dQGfWO+R1NJOFsiax3IS4Ng300SVp7Gz3czxT6d6qf2cw0g== + +esbuild-freebsd-64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz#09250f997a56ed4650f3e1979c905ffc40bbe94d" + integrity sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg== + +esbuild-freebsd-arm64@0.14.51: + version "0.14.51" + resolved "https://registry.npmmirror.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.51.tgz#63c435917e566808c71fafddc600aca4d78be1ec" + integrity sha512-TcWVw/rCL2F+jUgRkgLa3qltd5gzKjIMGhkVybkjk6PJadYInPtgtUBp1/hG+mxyigaT7ib+od1Xb84b+L+1Mg== + +esbuild-freebsd-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz#bafb46ed04fc5f97cbdb016d86947a79579f8e48" + integrity sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q== + +esbuild-linux-32@0.14.51: + version "0.14.51" + resolved "https://registry.npmmirror.com/esbuild-linux-32/-/esbuild-linux-32-0.14.51.tgz#c3da774143a37e7f11559b9369d98f11f997a5d9" + integrity sha512-RFqpyC5ChyWrjx8Xj2K0EC1aN0A37H6OJfmUXIASEqJoHcntuV3j2Efr9RNmUhMfNE6yEj2VpYuDteZLGDMr0w== + +esbuild-linux-32@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz#e2a8c4a8efdc355405325033fcebeb941f781fe5" + integrity sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw== + +esbuild-linux-64@0.14.51: + version "0.14.51" + resolved "https://registry.npmmirror.com/esbuild-linux-64/-/esbuild-linux-64-0.14.51.tgz#5d92b67f674e02ae0b4a9de9a757ba482115c4ae" + integrity sha512-dxjhrqo5i7Rq6DXwz5v+MEHVs9VNFItJmHBe1CxROWNf4miOGoQhqSG8StStbDkQ1Mtobg6ng+4fwByOhoQoeA== + +esbuild-linux-64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz#de5fdba1c95666cf72369f52b40b03be71226652" + integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg== + +esbuild-linux-arm64@0.14.51: + version "0.14.51" + resolved "https://registry.npmmirror.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.51.tgz#dac84740516e859d8b14e1ecc478dd5241b10c93" + integrity sha512-D9rFxGutoqQX3xJPxqd6o+kvYKeIbM0ifW2y0bgKk5HPgQQOo2k9/2Vpto3ybGYaFPCE5qTGtqQta9PoP6ZEzw== + +esbuild-linux-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz#dae4cd42ae9787468b6a5c158da4c84e83b0ce8b" + integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig== + +esbuild-linux-arm@0.14.51: + version "0.14.51" + resolved "https://registry.npmmirror.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.51.tgz#b3ae7000696cd53ed95b2b458554ff543a60e106" + integrity sha512-LsJynDxYF6Neg7ZC7748yweCDD+N8ByCv22/7IAZglIEniEkqdF4HCaa49JNDLw1UQGlYuhOB8ZT/MmcSWzcWg== + +esbuild-linux-arm@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz#a2c1dff6d0f21dbe8fc6998a122675533ddfcd59" + integrity sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw== + +esbuild-linux-mips64le@0.14.51: + version "0.14.51" + resolved "https://registry.npmmirror.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.51.tgz#dad10770fac94efa092b5a0643821c955a9dd385" + integrity sha512-vS54wQjy4IinLSlb5EIlLoln8buh1yDgliP4CuEHumrPk4PvvP4kTRIG4SzMXm6t19N0rIfT4bNdAxzJLg2k6A== + +esbuild-linux-mips64le@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz#d9918e9e4cb972f8d6dae8e8655bf9ee131eda34" + integrity sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw== + +esbuild-linux-ppc64le@0.14.51: + version "0.14.51" + resolved "https://registry.npmmirror.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.51.tgz#b68c2f8294d012a16a88073d67e976edd4850ae0" + integrity sha512-xcdd62Y3VfGoyphNP/aIV9LP+RzFw5M5Z7ja+zdpQHHvokJM7d0rlDRMN+iSSwvUymQkqZO+G/xjb4/75du8BQ== + +esbuild-linux-ppc64le@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz#3f9a0f6d41073fb1a640680845c7de52995f137e" + integrity sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ== + +esbuild-linux-riscv64@0.14.51: + version "0.14.51" + resolved "https://registry.npmmirror.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.51.tgz#608a318b8697123e44c1e185cdf6708e3df50b93" + integrity sha512-syXHGak9wkAnFz0gMmRBoy44JV0rp4kVCEA36P5MCeZcxFq8+fllBC2t6sKI23w3qd8Vwo9pTADCgjTSf3L3rA== + +esbuild-linux-riscv64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz#618853c028178a61837bc799d2013d4695e451c8" + integrity sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg== + +esbuild-linux-s390x@0.14.51: + version "0.14.51" + resolved "https://registry.npmmirror.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.51.tgz#c9e7791170a3295dba79b93aa452beb9838a8625" + integrity sha512-kFAJY3dv+Wq8o28K/C7xkZk/X34rgTwhknSsElIqoEo8armCOjMJ6NsMxm48KaWY2h2RUYGtQmr+RGuUPKBhyw== + +esbuild-linux-s390x@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz#d1885c4c5a76bbb5a0fe182e2c8c60eb9e29f2a6" + integrity sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA== + +esbuild-netbsd-64@0.14.51: + version "0.14.51" + resolved "https://registry.npmmirror.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.51.tgz#0abd40b8c2e37fda6f5cc41a04cb2b690823d891" + integrity sha512-ZZBI7qrR1FevdPBVHz/1GSk1x5GDL/iy42Zy8+neEm/HA7ma+hH/bwPEjeHXKWUDvM36CZpSL/fn1/y9/Hb+1A== + +esbuild-netbsd-64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz#69ae917a2ff241b7df1dbf22baf04bd330349e81" + integrity sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w== + +esbuild-openbsd-64@0.14.51: + version "0.14.51" + resolved "https://registry.npmmirror.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.51.tgz#4adba0b7ea7eb1428bb00d8e94c199a949b130e8" + integrity sha512-7R1/p39M+LSVQVgDVlcY1KKm6kFKjERSX1lipMG51NPcspJD1tmiZSmmBXoY5jhHIu6JL1QkFDTx94gMYK6vfA== + +esbuild-openbsd-64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz#db4c8495287a350a6790de22edea247a57c5d47b" + integrity sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw== + +esbuild-sunos-64@0.14.51: + version "0.14.51" + resolved "https://registry.npmmirror.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.51.tgz#4b8a6d97dfedda30a6e39607393c5c90ebf63891" + integrity sha512-HoHaCswHxLEYN8eBTtyO0bFEWvA3Kdb++hSQ/lLG7TyKF69TeSG0RNoBRAs45x/oCeWaTDntEZlYwAfQlhEtJA== + +esbuild-sunos-64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz#54287ee3da73d3844b721c21bc80c1dc7e1bf7da" + integrity sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw== + +esbuild-windows-32@0.14.51: + version "0.14.51" + resolved "https://registry.npmmirror.com/esbuild-windows-32/-/esbuild-windows-32-0.14.51.tgz#d31d8ca0c1d314fb1edea163685a423b62e9ac17" + integrity sha512-4rtwSAM35A07CBt1/X8RWieDj3ZUHQqUOaEo5ZBs69rt5WAFjP4aqCIobdqOy4FdhYw1yF8Z0xFBTyc9lgPtEg== + +esbuild-windows-32@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz#f8aaf9a5667630b40f0fb3aa37bf01bbd340ce31" + integrity sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w== + +esbuild-windows-64@0.14.51: + version "0.14.51" + resolved "https://registry.npmmirror.com/esbuild-windows-64/-/esbuild-windows-64-0.14.51.tgz#7d3c09c8652d222925625637bdc7e6c223e0085d" + integrity sha512-HoN/5HGRXJpWODprGCgKbdMvrC3A2gqvzewu2eECRw2sYxOUoh2TV1tS+G7bHNapPGI79woQJGV6pFH7GH7qnA== + +esbuild-windows-64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz#bf54b51bd3e9b0f1886ffdb224a4176031ea0af4" + integrity sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ== + +esbuild-windows-arm64@0.14.51: + version "0.14.51" + resolved "https://registry.npmmirror.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.51.tgz#0220d2304bfdc11bc27e19b2aaf56edf183e4ae9" + integrity sha512-JQDqPjuOH7o+BsKMSddMfmVJXrnYZxXDHsoLHc0xgmAZkOOCflRmC43q31pk79F9xuyWY45jDBPolb5ZgGOf9g== + +esbuild-windows-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz#937d15675a15e4b0e4fafdbaa3a01a776a2be982" + integrity sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg== + +esbuild@0.14.51: + version "0.14.51" + resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.14.51.tgz#1c8ecbc8db3710da03776211dc3ee3448f7aa51e" + integrity sha512-+CvnDitD7Q5sT7F+FM65sWkF8wJRf+j9fPcprxYV4j+ohmzVj2W7caUqH2s5kCaCJAfcAICjSlKhDCcvDpU7nw== + optionalDependencies: + esbuild-android-64 "0.14.51" + esbuild-android-arm64 "0.14.51" + esbuild-darwin-64 "0.14.51" + esbuild-darwin-arm64 "0.14.51" + esbuild-freebsd-64 "0.14.51" + esbuild-freebsd-arm64 "0.14.51" + esbuild-linux-32 "0.14.51" + esbuild-linux-64 "0.14.51" + esbuild-linux-arm "0.14.51" + esbuild-linux-arm64 "0.14.51" + esbuild-linux-mips64le "0.14.51" + esbuild-linux-ppc64le "0.14.51" + esbuild-linux-riscv64 "0.14.51" + esbuild-linux-s390x "0.14.51" + esbuild-netbsd-64 "0.14.51" + esbuild-openbsd-64 "0.14.51" + esbuild-sunos-64 "0.14.51" + esbuild-windows-32 "0.14.51" + esbuild-windows-64 "0.14.51" + esbuild-windows-arm64 "0.14.51" + +esbuild@^0.14.27: + version "0.14.54" + resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.14.54.tgz#8b44dcf2b0f1a66fc22459943dccf477535e9aa2" + integrity sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA== + optionalDependencies: + "@esbuild/linux-loong64" "0.14.54" + esbuild-android-64 "0.14.54" + esbuild-android-arm64 "0.14.54" + esbuild-darwin-64 "0.14.54" + esbuild-darwin-arm64 "0.14.54" + esbuild-freebsd-64 "0.14.54" + esbuild-freebsd-arm64 "0.14.54" + esbuild-linux-32 "0.14.54" + esbuild-linux-64 "0.14.54" + esbuild-linux-arm "0.14.54" + esbuild-linux-arm64 "0.14.54" + esbuild-linux-mips64le "0.14.54" + esbuild-linux-ppc64le "0.14.54" + esbuild-linux-riscv64 "0.14.54" + esbuild-linux-s390x "0.14.54" + esbuild-netbsd-64 "0.14.54" + esbuild-openbsd-64 "0.14.54" + esbuild-sunos-64 "0.14.54" + esbuild-windows-32 "0.14.54" + esbuild-windows-64 "0.14.54" + esbuild-windows-arm64 "0.14.54" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.npmmirror.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-html@^1.0.1, escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-config-prettier@^8.1.0: + version "8.8.0" + resolved "https://registry.npmmirror.com/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz#bfda738d412adc917fd7b038857110efe98c9348" + integrity sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA== + +eslint-plugin-vue@^9.0.0: + version "9.11.0" + resolved "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-9.11.0.tgz#99a247455c02181f24d9240d422380fd16dd630c" + integrity sha512-bBCJAZnkBV7ATH4Z1E7CvN3nmtS4H7QUU3UBxPdo8WohRU+yHjnQRALpTbxMVcz0e4Mx3IyxIdP5HYODMxK9cQ== + dependencies: + "@eslint-community/eslint-utils" "^4.3.0" + natural-compare "^1.4.0" + nth-check "^2.0.1" + postcss-selector-parser "^6.0.9" + semver "^7.3.5" + vue-eslint-parser "^9.0.1" + xml-name-validator "^4.0.0" + +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.1.1, eslint-scope@^7.2.0: + version "7.2.0" + resolved "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" + integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.0: + version "3.4.0" + resolved "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz#c7f0f956124ce677047ddbc192a68f999454dedc" + integrity sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ== + +eslint@^8.10.0: + version "8.39.0" + resolved "https://registry.npmmirror.com/eslint/-/eslint-8.39.0.tgz#7fd20a295ef92d43809e914b70c39fd5a23cf3f1" + integrity sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.4.0" + "@eslint/eslintrc" "^2.0.2" + "@eslint/js" "8.39.0" + "@humanwhocodes/config-array" "^0.11.8" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.0" + eslint-visitor-keys "^3.4.0" + espree "^9.5.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + grapheme-splitter "^1.0.4" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-sdsl "^4.1.4" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.1" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + +espree@^9.3.1, espree@^9.5.1: + version "9.5.1" + resolved "https://registry.npmmirror.com/espree/-/espree-9.5.1.tgz#4f26a4d5f18905bf4f2e0bd99002aab807e96dd4" + integrity sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg== + dependencies: + acorn "^8.8.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.0" + +esquery@^1.4.0, esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.npmmirror.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.npmmirror.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +estree-walker@^2.0.1, estree-walker@^2.0.2: + version "2.0.2" + resolved "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +express@^4.17.3: + version "4.18.2" + resolved "https://registry.npmmirror.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.1" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.5.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.npmmirror.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@3.2.12, fast-glob@^3.2.9: + version "3.2.12" + resolved "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" + integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.15.0" + resolved "https://registry.npmmirror.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + dependencies: + reusify "^1.0.4" + +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.npmmirror.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +filelist@^1.0.1: + version "1.0.4" + resolved "https://registry.npmmirror.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" + integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== + dependencies: + minimatch "^5.0.1" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.npmmirror.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + +flatted@^3.1.0: + version "3.2.7" + resolved "https://registry.npmmirror.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" + integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fraction.js@^4.2.0: + version "4.2.0" + resolved "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" + integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-extra@^11.1.0: + version "11.1.1" + resolved "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" + integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.0.2: + version "1.2.0" + resolved "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" + integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.3" + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@^7.1.3, glob@^7.1.4: + version "7.2.3" + resolved "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^13.19.0: + version "13.20.0" + resolved "https://registry.npmmirror.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" + integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== + dependencies: + type-fest "^0.20.2" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.npmmirror.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +google-protobuf@^3.21.2: + version "3.21.2" + resolved "https://registry.npmmirror.com/google-protobuf/-/google-protobuf-3.21.2.tgz#4580a2bea8bbb291ee579d1fefb14d6fa3070ea4" + integrity sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA== + +graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.11" + resolved "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +grapheme-splitter@^1.0.4: + version "1.0.4" + resolved "https://registry.npmmirror.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" + integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.npmmirror.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +he@^1.2.0: + version "1.2.0" + resolved "https://registry.npmmirror.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +html-minifier@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/html-minifier/-/html-minifier-4.0.0.tgz#cca9aad8bce1175e02e17a8c33e46d8988889f56" + integrity sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig== + dependencies: + camel-case "^3.0.0" + clean-css "^4.2.1" + commander "^2.19.0" + he "^1.2.0" + param-case "^2.1.1" + relateurl "^0.2.7" + uglify-js "^3.5.1" + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +iconv-lite@0.4.24, iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^5.2.0: + version "5.2.4" + resolved "https://registry.npmmirror.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + +import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inquirer@^8.2.1: + version "8.2.5" + resolved "https://registry.npmmirror.com/inquirer/-/inquirer-8.2.5.tgz#d8654a7542c35a9b9e069d27e2df4858784d54f8" + integrity sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.1" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.21" + mute-stream "0.0.8" + ora "^5.4.1" + run-async "^2.4.0" + rxjs "^7.5.5" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + wrap-ansi "^7.0.0" + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-core-module@^2.11.0: + version "2.12.0" + resolved "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.12.0.tgz#36ad62f6f73c8253fd6472517a12483cf03e7ec4" + integrity sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ== + dependencies: + has "^1.0.3" + +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.npmmirror.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.npmmirror.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.npmmirror.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isbinaryfile@^5.0.0: + version "5.0.0" + resolved "https://registry.npmmirror.com/isbinaryfile/-/isbinaryfile-5.0.0.tgz#034b7e54989dab8986598cbcea41f66663c65234" + integrity sha512-UDdnyGvMajJUWCkib7Cei/dvyJrrvo4FIrsvSFWdPpXSUorzXrDJ0S+X5Q4ZlasfPjca4yqCNNsjbCeiy8FFeg== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +ismobilejs@^1.1.0: + version "1.1.1" + resolved "https://registry.npmmirror.com/ismobilejs/-/ismobilejs-1.1.1.tgz#c56ca0ae8e52b24ca0f22ba5ef3215a2ddbbaa0e" + integrity sha512-VaFW53yt8QO61k2WJui0dHf4SlL8lxBofUuUmwBo0ljPk0Drz2TiuDW4jo3wDcv41qy/SxrJ+VAzJ/qYqsmzRw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +jake@^10.8.5: + version "10.8.5" + resolved "https://registry.npmmirror.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" + integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw== + dependencies: + async "^3.2.3" + chalk "^4.0.2" + filelist "^1.0.1" + minimatch "^3.0.4" + +js-base64@^3.7.5: + version "3.7.5" + resolved "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.5.tgz#21e24cf6b886f76d6f5f165bfcd69cc55b9e3fca" + integrity sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA== + +js-sdsl@^4.1.4: + version "4.4.0" + resolved "https://registry.npmmirror.com/js-sdsl/-/js-sdsl-4.4.0.tgz#8b437dbe642daa95760400b602378ed8ffea8430" + integrity sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.npmmirror.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kolorist@^1.5.1: + version "1.8.0" + resolved "https://registry.npmmirror.com/kolorist/-/kolorist-1.8.0.tgz#edddbbbc7894bc13302cdf740af6374d4a04743c" + integrity sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ== + +lazystream@^1.0.0: + version "1.0.1" + resolved "https://registry.npmmirror.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638" + integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw== + dependencies: + readable-stream "^2.0.5" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.defaults@^4.2.0: + version "4.2.0" + resolved "https://registry.npmmirror.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== + +lodash.difference@^4.5.0: + version "4.5.0" + resolved "https://registry.npmmirror.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c" + integrity sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA== + +lodash.flatten@^4.4.0: + version "4.4.0" + resolved "https://registry.npmmirror.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + integrity sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.npmmirror.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.npmmirror.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== + +lodash.union@^4.6.0: + version "4.6.0" + resolved "https://registry.npmmirror.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" + integrity sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw== + +lodash@^4.17.10, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.npmmirror.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +lower-case@^1.1.1: + version "1.1.4" + resolved "https://registry.npmmirror.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + integrity sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA== + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +magic-string@^0.25.7: + version "0.25.9" + resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" + integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== + dependencies: + sourcemap-codec "^1.4.8" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.npmmirror.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": + version "1.52.0" + resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1, minimatch@^5.1.0: + version "5.1.6" + resolved "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3: + version "2.1.3" + resolved "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.npmmirror.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +nanoid@^3.3.6: + version "3.3.6" + resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" + integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== + +natural-compare-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.npmmirror.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +no-case@^2.2.0: + version "2.3.2" + resolved "https://registry.npmmirror.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" + integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== + dependencies: + lower-case "^1.1.1" + +node-releases@^2.0.8: + version "2.0.10" + resolved "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" + integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.npmmirror.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== + +nth-check@^2.0.1: + version "2.1.1" + resolved "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + +object-inspect@^1.9.0: + version "1.12.3" + resolved "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0, once@^1.4.0: + version "1.4.0" + resolved "https://registry.npmmirror.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +open@^8.4.0: + version "8.4.2" + resolved "https://registry.npmmirror.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.npmmirror.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" + +ora@^5.4.1: + version "5.4.1" + resolved "https://registry.npmmirror.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + +ouch@^2.0.0: + version "2.0.1" + resolved "https://registry.npmmirror.com/ouch/-/ouch-2.0.1.tgz#9107089819b99146e3d10da57e8f75b39f610993" + integrity sha512-SdkEqpEhsmkEpjTPSvB1DMA//w9ChMUr16m4TayNRVfaULzJ3AnNr3CI4cz1QSZ9a+E/g06c6SQzxjkIc3/GMw== + dependencies: + "@positron/stack-trace" "1.0.0" + ejs "^3.1.7" + escape-html "^1.0.1" + lodash "^4.17.10" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +param-case@^2.1.1: + version "2.1.1" + resolved "https://registry.npmmirror.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" + integrity sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w== + dependencies: + no-case "^2.2.0" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pinia@^2.0.11: + version "2.0.35" + resolved "https://registry.npmmirror.com/pinia/-/pinia-2.0.35.tgz#aa2597038bb55ea14ad689f83065d2814ebb8c10" + integrity sha512-P1IKKQWhxGXiiZ3atOaNI75bYlFUbRxtJdhPLX059Z7+b9Z04rnTZdSY8Aph1LA+/4QEMAYHsTQ638Wfe+6K5g== + dependencies: + "@vue/devtools-api" "^6.5.0" + vue-demi "*" + +pixi-viewport@^5.0.1: + version "5.0.1" + resolved "https://registry.npmmirror.com/pixi-viewport/-/pixi-viewport-5.0.1.tgz#42e3934bd1535c4e60a5b95d09d0cf00bd673917" + integrity sha512-fIILU9xztqGnhGF5SYfjn1Rir/7asWkJ8zSUay2hwzPrdGTWFtB4yiIlZDeFaLf7KHA04RRb2kI01Sy1kNksAw== + +pixi.js@^7.2.4: + version "7.2.4" + resolved "https://registry.npmmirror.com/pixi.js/-/pixi.js-7.2.4.tgz#4cd6776bf7f74a6c5e121dd1b59329e66be2ce49" + integrity sha512-nBH60meoLnHxoMFz17HoMxXS4uJpG5jwIdL+Gx2S11TzWgP3iKF+/WLOTrkSdyuQoQSdIBxVqpnYii0Wiox15A== + dependencies: + "@pixi/accessibility" "7.2.4" + "@pixi/app" "7.2.4" + "@pixi/assets" "7.2.4" + "@pixi/compressed-textures" "7.2.4" + "@pixi/core" "7.2.4" + "@pixi/display" "7.2.4" + "@pixi/events" "7.2.4" + "@pixi/extensions" "7.2.4" + "@pixi/extract" "7.2.4" + "@pixi/filter-alpha" "7.2.4" + "@pixi/filter-blur" "7.2.4" + "@pixi/filter-color-matrix" "7.2.4" + "@pixi/filter-displacement" "7.2.4" + "@pixi/filter-fxaa" "7.2.4" + "@pixi/filter-noise" "7.2.4" + "@pixi/graphics" "7.2.4" + "@pixi/mesh" "7.2.4" + "@pixi/mesh-extras" "7.2.4" + "@pixi/mixin-cache-as-bitmap" "7.2.4" + "@pixi/mixin-get-child-by-name" "7.2.4" + "@pixi/mixin-get-global-position" "7.2.4" + "@pixi/particle-container" "7.2.4" + "@pixi/prepare" "7.2.4" + "@pixi/sprite" "7.2.4" + "@pixi/sprite-animated" "7.2.4" + "@pixi/sprite-tiling" "7.2.4" + "@pixi/spritesheet" "7.2.4" + "@pixi/text" "7.2.4" + "@pixi/text-bitmap" "7.2.4" + "@pixi/text-html" "7.2.4" + +postcss-selector-parser@^6.0.9: + version "6.0.11" + resolved "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz#2e41dc39b7ad74046e1615185185cd0b17d0c8dc" + integrity sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@^8.1.10, postcss@^8.4.13: + version "8.4.23" + resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.23.tgz#df0aee9ac7c5e53e1075c24a3613496f9e6552ab" + integrity sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier@^2.5.1: + version "2.8.8" + resolved "https://registry.npmmirror.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.npmmirror.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== + +punycode@^2.1.0: + version "2.3.0" + resolved "https://registry.npmmirror.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.npmmirror.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +quasar@^2.6.0: + version "2.11.10" + resolved "https://registry.npmmirror.com/quasar/-/quasar-2.11.10.tgz#31bf49dd7995673b2116aa250bb0b019ddcae74f" + integrity sha512-pV7bMdY/FUmOvNhZ2XjKSXJH92fsDu0cU/z7a9roPKV54cW41N1en3sLATrirjPComyZnk4uXrMdGtXp8+IpCg== + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.npmmirror.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +readable-stream@^2.0.0, readable-stream@^2.0.5: + version "2.3.8" + resolved "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdir-glob@^1.0.0: + version "1.1.3" + resolved "https://registry.npmmirror.com/readdir-glob/-/readdir-glob-1.1.3.tgz#c3d831f51f5e7bfa62fa2ffbe4b508c640f09584" + integrity sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA== + dependencies: + minimatch "^5.1.0" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +register-service-worker@^1.7.2: + version "1.7.2" + resolved "https://registry.npmmirror.com/register-service-worker/-/register-service-worker-1.7.2.tgz#6516983e1ef790a98c4225af1216bc80941a4bd2" + integrity sha512-CiD3ZSanZqcMPRhtfct5K9f7i3OLCcBBWsJjLh1gW9RO/nS94sVzY59iS+fgYBOBqaBpf4EzfqUF3j9IG+xo8A== + +relateurl@^0.2.7: + version "0.2.7" + resolved "https://registry.npmmirror.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve@^1.22.0: + version "1.22.2" + resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== + dependencies: + is-core-module "^2.11.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rollup-plugin-visualizer@^5.5.4: + version "5.9.0" + resolved "https://registry.npmmirror.com/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.9.0.tgz#013ac54fb6a9d7c9019e7eb77eced673399e5a0b" + integrity sha512-bbDOv47+Bw4C/cgs0czZqfm8L82xOZssk4ayZjG40y9zbXclNk7YikrZTDao6p7+HDiGxrN0b65SgZiVm9k1Cg== + dependencies: + open "^8.4.0" + picomatch "^2.3.1" + source-map "^0.7.4" + yargs "^17.5.1" + +"rollup@>=2.59.0 <2.78.0": + version "2.77.3" + resolved "https://registry.npmmirror.com/rollup/-/rollup-2.77.3.tgz#8f00418d3a2740036e15deb653bed1a90ee0cc12" + integrity sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g== + optionalDependencies: + fsevents "~2.3.2" + +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.npmmirror.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +rxjs@^7.5.5: + version "7.8.0" + resolved "https://registry.npmmirror.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" + integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== + dependencies: + tslib "^2.1.0" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@5.2.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sass@1.32.12: + version "1.32.12" + resolved "https://registry.npmmirror.com/sass/-/sass-1.32.12.tgz#a2a47ad0f1c168222db5206444a30c12457abb9f" + integrity sha512-zmXn03k3hN0KaiVTjohgkg98C3UowhL1/VSGdj4/VAAiMKGQOE80PFPxFP2Kyq0OUskPKcY5lImkhBKEHlypJA== + dependencies: + chokidar ">=3.0.0 <4.0.0" + +sax@1.1.4: + version "1.1.4" + resolved "https://registry.npmmirror.com/sax/-/sax-1.1.4.tgz#74b6d33c9ae1e001510f179a91168588f1aedaa9" + integrity sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg== + +semver@^7.3.5, semver@^7.3.6, semver@^7.3.7: + version "7.5.0" + resolved "https://registry.npmmirror.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" + integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== + dependencies: + lru-cache "^6.0.0" + +send@0.18.0: + version "0.18.0" + resolved "https://registry.npmmirror.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serialize-javascript@^6.0.0: + version "6.0.1" + resolved "https://registry.npmmirror.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" + integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== + dependencies: + randombytes "^2.1.0" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.npmmirror.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.npmmirror.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.npmmirror.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.2: + version "3.0.7" + resolved "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map@^0.6.1, source-map@~0.6.0: + version "0.6.1" + resolved "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.4: + version "0.7.4" + resolved "https://registry.npmmirror.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + +sourcemap-codec@^1.4.8: + version "1.4.8" + resolved "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +table@^6.8.0: + version "6.8.1" + resolved "https://registry.npmmirror.com/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf" + integrity sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + +tar-stream@^2.2.0: + version "2.2.0" + resolved "https://registry.npmmirror.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.npmmirror.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.npmmirror.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.1.0: + version "2.5.0" + resolved "https://registry.npmmirror.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" + integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.npmmirror.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.npmmirror.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.npmmirror.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typescript@^4.5.4: + version "4.9.5" + resolved "https://registry.npmmirror.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + +uglify-js@^3.5.1: + version "3.17.4" + resolved "https://registry.npmmirror.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" + integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +update-browserslist-db@^1.0.10: + version "1.0.11" + resolved "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" + integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +upper-case@^1.1.1: + version "1.1.3" + resolved "https://registry.npmmirror.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + integrity sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA== + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.npmmirror.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ== + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +vite@^2.9.13: + version "2.9.15" + resolved "https://registry.npmmirror.com/vite/-/vite-2.9.15.tgz#2858dd5b2be26aa394a283e62324281892546f0b" + integrity sha512-fzMt2jK4vQ3yK56te3Kqpkaeq9DkcZfBbzHwYpobasvgYmP2SoAr6Aic05CsB4CzCZbsDv4sujX3pkEGhLabVQ== + dependencies: + esbuild "^0.14.27" + postcss "^8.4.13" + resolve "^1.22.0" + rollup ">=2.59.0 <2.78.0" + optionalDependencies: + fsevents "~2.3.2" + +vue-demi@*: + version "0.14.0" + resolved "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.0.tgz#dcfd9a9cf9bb62ada1582ec9042372cf67ca6190" + integrity sha512-gt58r2ogsNQeVoQ3EhoUAvUsH9xviydl0dWJj7dabBC/2L4uBId7ujtCwDRD0JhkGsV1i0CtfLAeyYKBht9oWg== + +vue-eslint-parser@^9.0.1: + version "9.1.1" + resolved "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-9.1.1.tgz#3f4859be7e9bb7edaa1dc7edb05abffee72bf3dd" + integrity sha512-C2aI/r85Q6tYcz4dpgvrs4wH/MqVrRAVIdpYedrxnATDHHkb+TroeRcDpKWGZCx/OcECMWfz7tVwQ8e+Opy6rA== + dependencies: + debug "^4.3.4" + eslint-scope "^7.1.1" + eslint-visitor-keys "^3.3.0" + espree "^9.3.1" + esquery "^1.4.0" + lodash "^4.17.21" + semver "^7.3.6" + +vue-router@^4.0.0: + version "4.1.6" + resolved "https://registry.npmmirror.com/vue-router/-/vue-router-4.1.6.tgz#b70303737e12b4814578d21d68d21618469375a1" + integrity sha512-DYWYwsG6xNPmLq/FmZn8Ip+qrhFEzA14EI12MsMgVxvHFDYvlr4NXpVF5hrRH1wVcDP8fGi5F4rxuJSl8/r+EQ== + dependencies: + "@vue/devtools-api" "^6.4.5" + +vue@^3.0.0: + version "3.2.47" + resolved "https://registry.npmmirror.com/vue/-/vue-3.2.47.tgz#3eb736cbc606fc87038dbba6a154707c8a34cff0" + integrity sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ== + dependencies: + "@vue/compiler-dom" "3.2.47" + "@vue/compiler-sfc" "3.2.47" + "@vue/runtime-dom" "3.2.47" + "@vue/server-renderer" "3.2.47" + "@vue/shared" "3.2.47" + +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== + dependencies: + defaults "^1.0.3" + +webpack-merge@^5.8.0: + version "5.8.0" + resolved "https://registry.npmmirror.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" + integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== + dependencies: + clone-deep "^4.0.1" + wildcard "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.npmmirror.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wildcard@^2.0.0: + version "2.0.1" + resolved "https://registry.npmmirror.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + +word-wrap@^1.2.3: + version "1.2.3" + resolved "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.5.1: + version "17.7.1" + resolved "https://registry.npmmirror.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967" + integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zip-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.npmmirror.com/zip-stream/-/zip-stream-4.1.0.tgz#51dd326571544e36aa3f756430b313576dc8fc79" + integrity sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A== + dependencies: + archiver-utils "^2.1.0" + compress-commons "^4.1.0" + readable-stream "^3.6.0"