commit c03fac9ee8d802597561a9cb13c6cd6d40f7207b Author: YIN Date: Tue Apr 14 14:43:23 2026 +0800 first commit diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 0000000..dc3bc09 --- /dev/null +++ b/.browserslistrc @@ -0,0 +1,4 @@ +> 1% +last 2 versions +not dead +not ie 11 diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 0000000..5654e89 --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,5 @@ +# Changesets + +Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works with multi-package repos, or single-package repos to help you version and publish your code. You can find the full documentation for it [in our repository](https://github.com/changesets/changesets) + +We have a quick list of common questions to get you started engaging with this project in [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 0000000..f954fb4 --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", + "changelog": [ + "@changesets/changelog-github", + { "repo": "vbenjs/vue-vben-admin" } + ], + "commit": false, + "fixed": [["@vben-core/*", "@vben/*"]], + "snapshot": { + "prereleaseTemplate": "{tag}-{datetime}" + }, + "privatePackages": { "version": true, "tag": true }, + "linked": [], + "access": "public", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "ignore": [] +} diff --git a/.commitlintrc.js b/.commitlintrc.js new file mode 100644 index 0000000..02e33fa --- /dev/null +++ b/.commitlintrc.js @@ -0,0 +1 @@ +export { default } from '@vben/commitlint-config'; diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..52b833a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +node_modules +.git +.gitignore +*.md +dist +.turbo +dist.zip diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..179aec6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset=utf-8 +end_of_line=lf +insert_final_newline=true +indent_style=space +indent_size=2 +max_line_length = 100 +trim_trailing_whitespace = true +quote_type = single + +[*.{yml,yaml,json}] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..d4e5bd3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +# https://docs.github.com/cn/get-started/getting-started-with-git/configuring-git-to-handle-line-endings + +# Automatically normalize line endings (to LF) for all text-based files. +* text=auto eol=lf + +# Declare files that will always have CRLF line endings on checkout. +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf + +# Denote all files that are truly binary and should not be modified. +*.{ico,png,jpg,jpeg,gif,webp,svg,woff,woff2} binary \ No newline at end of file diff --git a/.gitconfig b/.gitconfig new file mode 100644 index 0000000..4b28a69 --- /dev/null +++ b/.gitconfig @@ -0,0 +1,2 @@ +[core] + ignorecase = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ab475 --- /dev/null +++ b/.gitignore @@ -0,0 +1,54 @@ +node_modules +.DS_Store +dist +dist-ssr +dist.zip +dist.tar +dist.war +.nitro +.output +*-dist.zip +*-dist.tar +*-dist.war +coverage +*.local +**/.vitepress/cache +.cache +.turbo +.temp +dev-dist +.stylelintcache +yarn.lock +package-lock.json +pnpm-lock.yaml +.VSCodeCounter +**/backend-mock/data + +# local env files +.env.local +.env.*.local +.eslintcache + +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* +vite.config.mts.* +vite.config.mjs.* +vite.config.js.* +vite.config.ts.* + +# Editor directories and files +.idea +# .vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +# 排除自动生成的类型文件 +apps/web-antd/types/components.d.ts +.history diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 0000000..fb75b43 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,6 @@ +ports: + - port: 5555 + onOpen: open-preview +tasks: + - init: corepack enable && pnpm install + command: pnpm run dev:play diff --git a/.lintstagedrc.mjs b/.lintstagedrc.mjs new file mode 100644 index 0000000..94b0192 --- /dev/null +++ b/.lintstagedrc.mjs @@ -0,0 +1,20 @@ +export default { + '*.md': ['prettier --cache --ignore-unknown --write'], + '*.vue': [ + 'prettier --write', + 'eslint --cache --fix', + 'stylelint --fix --allow-empty-input', + ], + '*.{js,jsx,ts,tsx}': [ + 'prettier --cache --ignore-unknown --write', + 'eslint --cache --fix', + ], + '*.{scss,less,styl,html,vue,css}': [ + 'prettier --cache --ignore-unknown --write', + 'stylelint --fix --allow-empty-input', + ], + 'package.json': ['prettier --cache --write'], + '{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': [ + 'prettier --cache --write--parser json', + ], +}; diff --git a/.node-version b/.node-version new file mode 100644 index 0000000..48b14e6 --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +20.14.0 diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..f4a1ad4 --- /dev/null +++ b/.npmrc @@ -0,0 +1,13 @@ +registry = "https://registry.npmmirror.com" +public-hoist-pattern[]=husky +public-hoist-pattern[]=eslint +public-hoist-pattern[]=prettier +public-hoist-pattern[]=prettier-plugin-tailwindcss +public-hoist-pattern[]=stylelint +public-hoist-pattern[]=*postcss* +public-hoist-pattern[]=@commitlint/* +public-hoist-pattern[]=czg + +strict-peer-dependencies=false +auto-install-peers=true +dedupe-peer-dependents=true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..d0b0ca1 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,18 @@ +dist +dev-dist +.local +.output.js +node_modules +.nvmrc +coverage +CODEOWNERS +.nitro +.output + + +**/*.svg +**/*.sh + +public +.npmrc +*-lock.yaml diff --git a/.prettierrc.mjs b/.prettierrc.mjs new file mode 100644 index 0000000..3e25d2c --- /dev/null +++ b/.prettierrc.mjs @@ -0,0 +1 @@ +export { default } from '@vben/prettier-config'; diff --git a/.stylelintignore b/.stylelintignore new file mode 100644 index 0000000..f4b2db2 --- /dev/null +++ b/.stylelintignore @@ -0,0 +1,4 @@ +dist +public +__tests__ +coverage diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..f4675d5 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,31 @@ +{ + "recommendations": [ + // Vue 3 的语言支持 + "Vue.volar", + // 将 ESLint JavaScript 集成到 VS Code 中。 + "dbaeumer.vscode-eslint", + // Visual Studio Code 的官方 Stylelint 扩展 + "stylelint.vscode-stylelint", + // 使用 Prettier 的代码格式化程序 + "esbenp.prettier-vscode", + // 支持 dotenv 文件语法 + "mikestead.dotenv", + // 源代码的拼写检查器 + "streetsidesoftware.code-spell-checker", + // Tailwind CSS 的官方 VS Code 插件 + "bradlc.vscode-tailwindcss", + // iconify 图标插件 + "antfu.iconify", + // i18n 插件 + "Lokalise.i18n-ally", + // CSS 变量提示 + "vunguyentuan.vscode-css-variables", + // 在 package.json 中显示 PNPM catalog 的版本 + "antfu.pnpm-catalog-lens" + ], + "unwantedRecommendations": [ + // 和 volar 冲突 + "octref.vetur", + "antfu.unocss" + ] +} diff --git a/.vscode/global.code-snippets b/.vscode/global.code-snippets new file mode 100644 index 0000000..7604b01 --- /dev/null +++ b/.vscode/global.code-snippets @@ -0,0 +1,37 @@ +{ + "import": { + "scope": "javascript,typescript", + "prefix": "im", + "body": ["import { $2 } from '$1';"], + "description": "Import a module", + }, + "export-all": { + "scope": "javascript,typescript", + "prefix": "ex", + "body": ["export * from '$1';"], + "description": "Export a module", + }, + "vue-script-setup": { + "scope": "vue", + "prefix": "", + "const props = defineProps<{", + " modelValue?: boolean,", + "}>()", + "$1", + "", + "", + "", + ], + }, + "vue-computed": { + "scope": "javascript,typescript,vue", + "prefix": "com", + "body": ["computed(() => { $1 })"], + }, +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..e967330 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "name": "vben admin playground dev", + "request": "launch", + "url": "http://localhost:5555", + "env": { "NODE_ENV": "development" }, + "sourceMaps": true, + "webRoot": "${workspaceFolder}/playground" + }, + { + "type": "chrome", + "name": "vben admin antd dev", + "request": "launch", + "url": "http://localhost:5666", + "env": { "NODE_ENV": "development" }, + "sourceMaps": true, + "webRoot": "${workspaceFolder}/apps/web-antd" + }, + { + "type": "chrome", + "name": "vben admin ele dev", + "request": "launch", + "url": "http://localhost:5777", + "env": { "NODE_ENV": "development" }, + "sourceMaps": true, + "webRoot": "${workspaceFolder}/apps/web-ele" + }, + { + "type": "chrome", + "name": "vben admin naive dev", + "request": "launch", + "url": "http://localhost:5888", + "env": { "NODE_ENV": "development" }, + "sourceMaps": true, + "webRoot": "${workspaceFolder}/apps/web-naive" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..58dfd80 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,233 @@ +{ + "tailwindCSS.experimental.configFile": "internal/tailwind-config/src/index.ts", + // workbench + "workbench.list.smoothScrolling": true, + "workbench.startupEditor": "newUntitledFile", + "workbench.tree.indent": 10, + "workbench.editor.highlightModifiedTabs": true, + "workbench.editor.closeOnFileDelete": true, + "workbench.editor.limit.enabled": true, + "workbench.editor.limit.perEditorGroup": true, + "workbench.editor.limit.value": 5, + + // editor + "editor.tabSize": 2, + "editor.detectIndentation": false, + "editor.cursorBlinking": "expand", + "editor.largeFileOptimizations": false, + "editor.accessibilitySupport": "off", + "editor.cursorSmoothCaretAnimation": "on", + "editor.guides.bracketPairs": "active", + "editor.inlineSuggest.enabled": true, + "editor.suggestSelection": "recentlyUsedByPrefix", + "editor.acceptSuggestionOnEnter": "smart", + "editor.suggest.snippetsPreventQuickSuggestions": false, + "editor.stickyScroll.enabled": false, + "editor.hover.sticky": true, + "editor.suggest.insertMode": "replace", + "editor.bracketPairColorization.enabled": true, + "editor.autoClosingBrackets": "beforeWhitespace", + "editor.autoClosingDelete": "always", + "editor.autoClosingOvertype": "always", + "editor.autoClosingQuotes": "beforeWhitespace", + "editor.wordSeparators": "`~!@#%^&*()=+[{]}\\|;:'\",.<>/?", + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit", + "source.fixAll.stylelint": "explicit", + "source.organizeImports": "never" + }, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "[html]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[css]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[scss]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[markdown]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[jsonc]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[vue]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + // extensions + "extensions.ignoreRecommendations": true, + + // terminal + "terminal.integrated.cursorBlinking": true, + "terminal.integrated.persistentSessionReviveProcess": "never", + "terminal.integrated.tabs.enabled": true, + "terminal.integrated.scrollback": 10000, + "terminal.integrated.stickyScroll.enabled": false, + + // files + "files.eol": "\n", + "files.insertFinalNewline": true, + "files.simpleDialog.enable": true, + "files.associations": { + "*.ejs": "html", + "*.art": "html", + "**/tsconfig.json": "jsonc", + "*.json": "jsonc", + "package.json": "json" + }, + + "files.exclude": { + "**/.eslintcache": true, + "**/bower_components": true, + "**/.turbo": true, + "**/.idea": true, + "**/tmp": true, + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.stylelintcache": true, + "**/.DS_Store": true, + "**/vite.config.mts.*": true, + "**/tea.yaml": true, + "**/public/tinymce/**": true + }, + "files.watcherExclude": { + "**/.git/objects/**": true, + "**/.git/subtree-cache/**": true, + "**/.vscode/**": true, + "**/node_modules/**": true, + "**/tmp/**": true, + "**/bower_components/**": true, + "**/dist/**": true, + "**/yarn.lock": true + }, + + // search + "search.searchEditor.singleClickBehaviour": "peekDefinition", + "search.followSymlinks": false, + // 在使用搜索功能时,将这些文件夹/文件排除在外 + "search.exclude": { + "**/node_modules": true, + "**/*.log": true, + "**/*.log*": true, + "**/bower_components": true, + "**/dist": true, + "**/elehukouben": true, + "**/.git": true, + "**/.github": true, + "**/.gitignore": true, + "**/.svn": true, + "**/.DS_Store": true, + "**/.vitepress/cache": true, + "**/.idea": true, + "**/.vscode": false, + "**/.yarn": true, + "**/tmp": true, + "*.xml": true, + "out": true, + "dist": true, + "node_modules": true, + "CHANGELOG.md": true, + "**/pnpm-lock.yaml": true, + "**/yarn.lock": true + }, + + "debug.onTaskErrors": "debugAnyway", + "diffEditor.ignoreTrimWhitespace": false, + "npm.packageManager": "pnpm", + + "css.validate": false, + "less.validate": false, + "scss.validate": false, + + // extension + "emmet.showSuggestionsAsSnippets": true, + "emmet.triggerExpansionOnTab": false, + + "errorLens.enabledDiagnosticLevels": ["warning", "error"], + "errorLens.excludeBySource": ["cSpell", "Grammarly", "eslint"], + + "stylelint.enable": true, + "stylelint.packageManager": "pnpm", + "stylelint.validate": ["css", "less", "postcss", "scss", "vue"], + "stylelint.customSyntax": "postcss-html", + "stylelint.snippet": ["css", "less", "postcss", "scss", "vue"], + + "typescript.inlayHints.enumMemberValues.enabled": true, + "typescript.preferences.preferTypeOnlyAutoImports": true, + "typescript.preferences.includePackageJsonAutoImports": "on", + + "eslint.validate": [ + "javascript", + "typescript", + "javascriptreact", + "typescriptreact", + "vue", + "html", + "markdown", + "json", + "jsonc", + "json5" + ], + + "tailwindCSS.experimental.classRegex": [ + ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"] + ], + + "github.copilot.enable": { + "*": true, + "markdown": true, + "plaintext": false, + "yaml": false + }, + + "cssVariables.lookupFiles": ["packages/core/base/design/src/**/*.css"], + + "i18n-ally.localesPaths": [ + "packages/locales/src/langs", + "playground/src/locales/langs", + "apps/*/src/locales/langs" + ], + "i18n-ally.pathMatcher": "{locale}/{namespace}.{ext}", + "i18n-ally.enabledParsers": ["json"], + "i18n-ally.sourceLanguage": "zh-CN", + "i18n-ally.displayLanguage": "zh-CN", + "i18n-ally.enabledFrameworks": ["vue", "react"], + "i18n-ally.keystyle": "nested", + "i18n-ally.sortKeys": true, + "i18n-ally.namespace": true, + + // 控制相关文件嵌套展示 + "explorer.fileNesting.enabled": true, + "explorer.fileNesting.expand": false, + "explorer.fileNesting.patterns": { + "*.ts": "$(capture).test.ts, $(capture).test.tsx, $(capture).spec.ts, $(capture).spec.tsx, $(capture).d.ts", + "*.tsx": "$(capture).test.ts, $(capture).test.tsx, $(capture).spec.ts, $(capture).spec.tsx,$(capture).d.ts", + "*.env": "$(capture).env.*", + "README.md": "README*,CHANGELOG*,LICENSE,CNAME", + "package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,.gitattributes,.gitignore,.gitpod.yml,.npmrc,.browserslistrc,.node-version,.git*,.tazerc.json", + "eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,cspell.json", + "tailwind.config.mjs": "postcss.*" + }, + "commentTranslate.hover.enabled": false, + "commentTranslate.multiLineMerge": true, + "vue.server.hybridMode": true, + "vitest.disableWorkspaceWarning": true, + "cSpell.words": ["tinymce", "vditor"], + "typescript.tsdk": "node_modules/typescript/lib", + "editor.linkedEditing": true, // 自动同步更改html标签, + "vscodeCustomCodeColor.highlightValue": "v-access", // v-access显示的颜色 + "vscodeCustomCodeColor.highlightValueColor": "#CCFFFF", + "oxc.enable": false +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..61eb2b1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,151 @@ +# 1.2.2 + +**FEATURES** + +- 代码生成支持路径方式生成 + +# 1.2.1 + +# BUG FIXES + +- 客户端管理 错误的status disabled +- modal/drawer升级后zIndex(2000)会遮挡Tinymce的下拉框zIndex(1300) + +# 1.2.0 + +**REFACTOR** + +- 菜单选择组件重构为Table形式 +- 字典相关功能重构 采用一个Map储存字典(之前为两个Map) +- 代码生成配置页面重构 去除步骤条 + +**Features** + +- 对接后端工作流 +- ~~通用的vxe-table排序事件(排序逻辑改为在排序事件中处理而非在api处理)~~ +- getDict/getDictOptions 提取公共逻辑 减少冗余代码 +- 字典新增对Number类型的支持 -> `getDictOptions('', true);`即可获取number类型的value +- 文件上传 增加上传进度条 下方上传提示 +- 图片上传 增加上传进度条 下方上传提示 +- oss下载进度提示 + +**BUG FIXES** + +- 字典项为空时getDict方法无限调用接口(无奈兼容 不给字典item本来就是错误用法) +- 表格排序翻页会丢失排序参数 +- 下载文件时(responseType === 'blob')需要判断下载失败(返回json而非二进制)的情况 +- requestClient缺失i18n内容 + +**OTHERS** + +- 用户管理 新增只获取一次(mounted)默认密码而非每次打开modal都获取 +- `apps/web-antd/src/utils/dict.ts` `getDict`方法将于下个版本删除 使用`getDictOptions`替代 +- VxeTable升级V4.10.0 +- 移除`@deprecated` `apps/web-antd/src/adapter/vxe-table.ts`的`tableCheckboxEvent`方法 +- 移除`由于更新方案弃用的` `apps/web-antd/src/adapter/vxe-table.ts`的`vxeSortEvent`方法 +- 移除apps下的ele和naive目录 + +# 1.1.3 + +**REFACTOR** + +- 重构: 判断vxe-table的复选框是否选中 + +**Bug Fixes** + +- 节点树在编辑 & 空数组(不勾选)情况 勾选节点会造成watch延迟触发 导致会带上父节点id造成id重复 +- 节点树在节点独立情况下的控制台warning: Invalid prop: type check failed for prop "value". Expected Array, got Object + +**Others** + +- 角色管理 优化Drawer布局 +- unplugin-vue-components插件(默认未开启) 需要排除Button组件 全局已经默认导入了 + +**BUG FIXES** + +- 操作日志详情 在description组件中json预览样式异常 +- 微服务版本 区间查询和中文搜索条件一起使用 无法正确查询 + +# 1.1.2 + +**Features** + +- Options转Enum工具函数 + +**OTHERS** + +- 菜单管理 改为虚拟滚动 +- 移除requestClient的一些冗余参数 +- 主动退出登录(右上角个人选项)不需要带跳转地址 + +**BUG FIXES** + +- 语言 漏加Content-Language请求头 +- 用户管理/岗位管理 左边部门树错误emit导致会调用两次列表api + +# 1.1.1 + +**REFACTOR** + +- 使用VxeTable重构OAuth账号绑定列表(替代antdv的Table) +- commonDownloadExcel方法 支持处理区间选择器字段导出excel + +**BUG FIXES** + +- 修复在Modal/Drawer中使用VxeTable时, 第二次打开表单参数依旧为第一次提交的参数 + +**OTHERS** + +- 废弃downloadExcel方法 统一使用commonDownloadExcel方法 + +# 1.1.0 + +**FEATURES** + +- 支持离线图标功能(全局可在内网环境中使用) + +**BUG FIXES** + +- 在VxeTable固定列时, getPopupContainer会导致宽度不够, 弹出层样式异常 解决办法(将弹窗元素挂载到VXe滚动容器上) + +**OTHERS** + +- 代码生成 - 字段信息修改 改为minWidth 防止在高分辨率屏幕出现空白 + +# 1.0.0 + +**FEATURES** + +- 用户管理 新增从参数取默认密码 +- 全局表格加上id 方便进行缓存列排序的操作 +- 支持菜单名称i18n +- 登录页 验证码登录 +- Markdown编辑/预览组件(基于vditor) +- VxeTable搜索表单 enter提交 + +**BUG FIXES** + +- 登录页面 关闭租户后下拉框没有正常隐藏 +- 字典管理 关闭租户不应显示`同步租户字典`按钮 +- 登录日志 漏掉了登录日志日期查询 +- 登出相关逻辑在并发(非await)情况下重复执行的问题 +- VxeTable在开启/关闭查询表单时 需要使用不同的padding +- VxeTable表格刷新 默认为reload 修改为在当前页刷新(query) +- 岗位管理 部门参数错误 +- 角色管理 菜单分配 节点独立下的回显及提交问题 +- 租户管理 套餐管理 回显时候`已选中节点`数量为0 +- 用户管理 更新用户时打开drawer需要加载该部门下的岗位信息 + +**OTHERS** + +- 登录页 租户选择框浮层固定高度[256px] 超过高度自动滚动 +- 表单的Label默认方向改为`top` 支持\n换行 +- 所有表格的搜索加上allowClear属性 支持清除 +- vxe表格loading 只加载表格 不加载上面的表单 + +# 1.0.0-beta (2024-10-8) + +**FEATURES** + +- 基础功能已经开发完毕 +- 工作流相关模块等待后端重构后开发 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cec5b42 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2024-present, Vben + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6eef0f1 --- /dev/null +++ b/README.md @@ -0,0 +1,156 @@ +
VbenAdmin Logo

+ +[![license](https://img.shields.io/github/license/anncwb/vue-vben-admin.svg)](LICENSE) + +

Vue Vben Admin

+
+ +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=vbenjs_vue-vben-admin&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=vbenjs_vue-vben-admin) ![codeql](https://github.com/vbenjs/vue-vben-admin/actions/workflows/codeql.yml/badge.svg) ![build](https://github.com/vbenjs/vue-vben-admin/actions/workflows/build.yml/badge.svg) ![ci](https://github.com/vbenjs/vue-vben-admin/actions/workflows/ci.yml/badge.svg) ![deploy](https://github.com/vbenjs/vue-vben-admin/actions/workflows/deploy.yml/badge.svg) + +**English** | [中文](./README.zh-CN.md) | [日本語](./README.ja-JP.md) + +## Introduction + +Vue Vben Admin is a free and open source middle and back-end template. Using the latest `vue3`, `vite`, `TypeScript` and other mainstream technology development, the out-of-the-box middle and back-end front-end solutions can also be used for learning reference. + +## Upgrade Notice + +This is the latest version, 5.0, and it is not compatible with previous versions. If you are starting a new project, it is recommended to use the latest version. If you wish to view the old version, please use the [v2 branch](https://github.com/vbenjs/vue-vben-admin/tree/v2). + +## Feature + +- **Latest Technology Stack**: Developed with cutting-edge front-end technologies like Vue 3 and Vite +- **TypeScript**: A language for application-scale JavaScript +- **Themes**: Multiple theme colors available with customizable options +- **Internationalization**: Comprehensive built-in internationalization support +- **Permissions**: Built-in solution for dynamic route-based permission generation + +## Preview + +- [Vben Admin](https://vben.pro/) - Full version Chinese site + +Test Account: vben/123456 + +

+ VbenAdmin Logo + VbenAdmin Logo + VbenAdmin Logo +

+ +### Use Gitpod + +Open the project in Gitpod (free online dev environment for GitHub) and start coding immediately. + +[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/vbenjs/vue-vben-admin) + +## Documentation + +[Document](https://doc.vben.pro/) + +## Install and use + +- Get the project code + +```bash +git clone https://github.com/vbenjs/vue-vben-admin.git +``` + +- Installation dependencies + +```bash +cd vue-vben-admin + +corepack enable + +pnpm install +``` + +- run + +```bash +pnpm dev +``` + +- build + +```bash +pnpm build +``` + +## Change Log + +[CHANGELOG](https://github.com/vbenjs/vue-vben-admin/releases) + +## How to contribute + +You are very welcome to join![Raise an issue](https://github.com/anncwb/vue-vben-admin/issues/new/choose) Or submit a Pull Request。 + +**Pull Request:** + +1. Fork code! +2. Create your own branch: `git checkout -b feat/xxxx` +3. Submit your changes: `git commit -am 'feat(function): add xxxxx'` +4. Push your branch: `git push origin feat/xxxx` +5. submit`pull request` + +## Git Contribution submission specification + +- reference [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) specification ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular)) + + - `feat` Add new features + - `fix` Fix the problem/BUG + - `style` The code style is related and does not affect the running result + - `perf` Optimization/performance improvement + - `refactor` Refactor + - `revert` Undo edit + - `test` Test related + - `docs` Documentation/notes + - `chore` Dependency update/scaffolding configuration modification etc. + - `ci` Continuous integration + - `types` Type definition file changes + - `wip` In development + +## Browser support + +The `Chrome 80+` browser is recommended for local development + +Support modern browsers, not IE + +| [ Edge](http://godban.github.io/browsers-support-badges/)
IE | [ Edge](http://godban.github.io/browsers-support-badges/)
Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
Safari | +| :-: | :-: | :-: | :-: | :-: | +| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions | + +## Maintainer + +[@Vben](https://github.com/anncwb) + +## Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=vbenjs/vue-vben-admin&type=Date)](https://star-history.com/#vbenjs/vue-vben-admin&Date) + +## Donate + +If you think this project is helpful to you, you can help the author buy a cup of coffee to show your support! + +![donate](https://unpkg.com/@vbenjs/static-source@0.1.7/source/sponsor.png) + +Paypal Me + +## Contributor + + + Contributors + + +## Discord + +- [Github Discussions](https://github.com/anncwb/vue-vben-admin/discussions) + +## License + +[MIT © Vben-2020](./LICENSE) + + + +图标更改E:\HF-Worker\Vben5-HCZZ\apps\web-antd\node_modules\@vben\icons\node_modules\@vben-core\icons\node_modules\@iconify\vue\dist\iconify.js diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 0000000..6ba1448 --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,152 @@ + + +## 提示 + +该仓库使用vben最新版本v5开发 + +v5版本采用分仓(包)目录结构, 具体开发路径为: `根目录/apps/web-antd` + +目前对应后端版本: **分布式5.3.0/微服务2.2.2** + +V1.1.0版本已支持离线图标 + + +## 简介 + +基于 [vben5 & ant-design-vue](https://github.com/vbenjs/vue-vben-admin) 的 RuoYi-Vue-Plus 前端项目 + +| 组件/框架 | 版本 | +| :------------- | :----- | +| vben | 5.5.3 | +| ant-design-vue | 4.2.6 | +| vue | 3.5.13 | + +对应后端项目: **(分布式 5.X 分支 微服务 2.分支)** + +分布式 [RuoYi-Vue-Plus](https://gitee.com/dromara/RuoYi-Vue-Plus/tree/5.X/) + +微服务 [RuoYi-Cloud-Plus](https://gitee.com/dromara/RuoYi-Cloud-Plus/tree/2.X/) + + +## 文档 + +[Vben V5 文档地址](https://doc.vben.pro/) + + +## 安装使用 + +前置准备环境(只能用pnpm) + +```json +"packageManager": "pnpm", +"engines": { + "node": ">=20.15.0", + "pnpm": "latest" +}, +``` + +- 获取项目代码 + +```bash +git clone http://git.helou.vip:8088/ruansi/Vben5-Base.git +``` + +- 安装依赖 + +```bash +cd Vben5-Base + +pnpm install +``` + +- 菜单图标替换 + +参考 [菜单图标替换](https://dapdap.top/guide/quick-start.html#%E8%8F%9C%E5%8D%95%E5%9B%BE%E6%A0%87%E5%AF%BC%E5%85%A5) + + +- 关于一些监控的地址配置(微服务版本可以跳过这一小节) + +使用[RuoYi-Vue-Plus](https://gitee.com/dromara/RuoYi-Vue-Plus/tree/5.X/)注意 `已经去除 admin/snailjob 的.env 配置` 可自行修改 有两种方式 + +1. 修改源码`/views/monitor/admin` `views/monitor/snailjob` + +```html + + +``` + +2. **推荐** 使用菜单自行配置 (跟 cloud 版本打开方式一致) + +![图片](https://gitee.com/dapppp/ruoyi-plus-vben/raw/main/preview/菜单修改.png) + +使用内嵌 iframe 方式需要解决跨域问题 可参考[nginx.conf](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/5.X/script/docker/nginx/conf/nginx.conf#LC87)配置 + +- 修改.env.development 配置文件 +- **注意 RSA 公私钥一定要修改和后端匹配** +- RSA 公私钥为两对 `前端请求加密-后端解密是一对` `后端响应加密 前端解密是一对` + +```properties +# 端口号 +VITE_PORT=5666 +# 打包路径 +VITE_BASE=/ +# 是否开启 Nitro Mock服务,true 为开启,false 为关闭 +VITE_NITRO_MOCK=false +# 是否打开 devtools,true 为打开,false 为关闭 +VITE_DEVTOOLS=false +# 是否注入全局loading +VITE_INJECT_APP_LOADING=true + +# 后台请求路径 具体在vite.config.mts配置代理 +VITE_GLOB_API_URL=/api +# 全局加密开关(即开启了加解密功能才会生效 不是全部接口加密 需要和后端对应) +VITE_GLOB_ENABLE_ENCRYPT=true +# RSA公钥 请求加密使用 注意这两个是两对RSA公私钥 请求加密-后端解密是一对 响应解密-后端加密是一对 +VITE_GLOB_RSA_PUBLIC_KEY= +# RSA私钥 响应解密使用 注意这两个是两对RSA公私钥 请求加密-后端解密是一对 响应解密-后端加密是一对 +VITE_GLOB_RSA_PRIVATE_KEY= +# 客户端id +VITE_GLOB_APP_CLIENT_ID=e5cd7e4891bf95d1d19206ce24a7b32e +# 开启WEBSOCKET +VITE_GLOB_WEBSOCKET_ENABLE=false +``` + +- 运行 + +```bash +pnpm dev:antd +``` + +- 打包 + +```bash +pnpm build:antd +``` + +## 这是一个特性 而不是一个bug! + +1. 菜单管理可分配 但只有`admin`/`superadmin`角色能访问 其他角色访问会到403页面 +2. 租户相关菜单可分配 但只有`superadmin`角色能访问 其他角色访问会到403页面 +3. 分配的租户管理员无法修改自己的角色的菜单(即管理员角色的菜单) 防止自己把自己权限弄没了 + + + +## 浏览器支持 + +最低适配应该为`Chrome 88+`以上浏览器 详见 [css - where](https://developer.mozilla.org/en-US/docs/Web/CSS/:where#browser_compatibility) + +本地开发推荐使用`Chrome` 最新版本浏览器 + +支持现代浏览器, 不支持 IE + diff --git a/apps/web-antd/.env b/apps/web-antd/.env new file mode 100644 index 0000000..1a714b3 --- /dev/null +++ b/apps/web-antd/.env @@ -0,0 +1,10 @@ +# 应用标题 +VITE_APP_TITLE=合成作战指挥调度系统 + +# 应用命名空间,用于缓存、store等功能的前缀,确保隔离 +VITE_APP_NAMESPACE=vben-web-antd + +# 聊天服务地址 + +# VITE_APP_CHAT_SERVER_URL=http://53.155.32.160:8080 +VITE_APP_CHAT_SERVER_URL=http://192.168.0.58:8080 diff --git a/apps/web-antd/.env.analyze b/apps/web-antd/.env.analyze new file mode 100644 index 0000000..ffafa8d --- /dev/null +++ b/apps/web-antd/.env.analyze @@ -0,0 +1,7 @@ +# public path +VITE_BASE=/ + +# Basic interface address SPA +VITE_GLOB_API_URL=/api + +VITE_VISUALIZER=true diff --git a/apps/web-antd/.env.development b/apps/web-antd/.env.development new file mode 100644 index 0000000..491a840 --- /dev/null +++ b/apps/web-antd/.env.development @@ -0,0 +1,32 @@ +### + # @Author: Mm + # @Date: 2025-05-06 14:08:27 + # @LastEditors: Mm + # @LastEditTime: 2025-07-30 14:59:38 + # 不写bug的程序员不是一个好程序员! +### +# 端口号 +VITE_PORT=9528 + +VITE_BASE=/ +# 是否开启 Nitro Mock服务,true 为开启,false 为关闭 +VITE_NITRO_MOCK=false +# 是否打开 devtools,true 为打开,false 为关闭 +VITE_DEVTOOLS=false +# 是否注入全局loading +VITE_INJECT_APP_LOADING=true + +# 后台请求路径 具体在vite.config.mts配置代理 +VITE_GLOB_API_URL=/api + +# 全局加密开关(即开启了加解密功能才会生效 不是全部接口加密 需要和后端对应) +VITE_GLOB_ENABLE_ENCRYPT=true +# RSA公钥 请求加密使用 注意这两个是两对RSA公私钥 请求加密-后端解密是一对 响应解密-后端加密是一对 +VITE_GLOB_RSA_PUBLIC_KEY=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ== +# RSA私钥 响应解密使用 注意这两个是两对RSA公私钥 请求加密-后端解密是一对 响应解密-后端加密是一对 +VITE_GLOB_RSA_PRIVATE_KEY=MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE= +# 客户端id +VITE_GLOB_APP_CLIENT_ID=e5cd7e4891bf95d1d19206ce24a7b32e + +# 开启SSE +VITE_GLOB_SSE_ENABLE=true diff --git a/apps/web-antd/.env.production b/apps/web-antd/.env.production new file mode 100644 index 0000000..d9298a0 --- /dev/null +++ b/apps/web-antd/.env.production @@ -0,0 +1,43 @@ +### + # @Author: Mm + # @Date: 2025-05-06 14:08:27 + # @LastEditors: Mm + # @LastEditTime: 2025-06-18 10:36:37 + # 不写bug的程序员不是一个好程序员! +### +VITE_BASE=/ + +# 是否开启压缩,可以设置为 none, brotli, gzip +VITE_COMPRESS=gzip + +# 是否开启 PWA +VITE_PWA=false + +# vue-router 的模式 +VITE_ROUTER_HISTORY=history + +# 是否注入全局loading +VITE_INJECT_APP_LOADING=true + +# 打包后是否生成dist.zip +VITE_ARCHIVER=true + +# 后台请求路径 具体在vite.config.mts配置代理 +VITE_GLOB_API_URL=/prod-api + +# 全局加密开关(即开启了加解密功能才会生效 不是全部接口加密 需要和后端对应) +VITE_GLOB_ENABLE_ENCRYPT=true +# RSA公钥 请求加密使用 注意这两个是两对RSA公私钥 请求加密-后端解密是一对 响应解密-后端加密是一对 +VITE_GLOB_RSA_PUBLIC_KEY=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ== +# RSA私钥 响应解密使用 注意这两个是两对RSA公私钥 请求加密-后端解密是一对 响应解密-后端加密是一对 +VITE_GLOB_RSA_PRIVATE_KEY=MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE= +# 客户端id +VITE_GLOB_APP_CLIENT_ID=e5cd7e4891bf95d1d19206ce24a7b32e + +# 开启SSE +VITE_GLOB_SSE_ENABLE=true + + +# iconify图标请求地址 +VITE_ICONIFY_API=http://53.1.252.95:8080 + diff --git a/apps/web-antd/index.html b/apps/web-antd/index.html new file mode 100644 index 0000000..33d34a9 --- /dev/null +++ b/apps/web-antd/index.html @@ -0,0 +1,22 @@ + + + + + + + + + + + + <%= VITE_APP_TITLE %> + + + +
+ + + diff --git a/apps/web-antd/package.json b/apps/web-antd/package.json new file mode 100644 index 0000000..2a8206b --- /dev/null +++ b/apps/web-antd/package.json @@ -0,0 +1,64 @@ +{ + "name": "@vben/web-antd", + "version": "1.2.1", + "homepage": "https://vben.pro", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "apps/web-antd" + }, + "license": "MIT", + "author": { + "name": "vben", + "email": "ann.vben@gmail.com", + "url": "https://github.com/anncwb" + }, + "type": "module", + "scripts": { + "build": "pnpm vite build --mode production", + "build:analyze": "pnpm vite build --mode analyze", + "dev": "pnpm vite --mode development", + "preview": "vite preview", + "typecheck": "vue-tsc --noEmit --skipLibCheck" + }, + "imports": { + "#/*": "./src/*" + }, + "dependencies": { + "@ant-design/icons-vue": "^7.0.1", + "@tinymce/tinymce-vue": "^6.0.1", + "@vben-core/shadcn-ui": "workspace:*", + "@vben/access": "workspace:*", + "@vben/common-ui": "workspace:*", + "@vben/constants": "workspace:*", + "@vben/hooks": "workspace:*", + "@vben/icons": "workspace:*", + "@vben/layouts": "workspace:*", + "@vben/locales": "workspace:*", + "@vben/plugins": "workspace:*", + "@vben/preferences": "workspace:*", + "@vben/request": "workspace:*", + "@vben/stores": "workspace:*", + "@vben/styles": "workspace:*", + "@vben/types": "workspace:*", + "@vben/utils": "workspace:*", + "@vueuse/core": "catalog:", + "ant-design-vue": "catalog:", + "cropperjs": "^1.6.2", + "crypto-js": "^4.2.0", + "dayjs": "catalog:", + "echarts": "^5.5.1", + "jsencrypt": "^3.3.2", + "lodash-es": "^4.17.21", + "pinia": "catalog:", + "tinymce": "^7.3.0", + "unplugin-vue-components": "^0.27.3", + "vue": "catalog:", + "vue-router": "catalog:" + }, + "devDependencies": { + "@types/crypto-js": "^4.2.2", + "@types/lodash-es": "^4.17.12" + } +} diff --git a/apps/web-antd/postcss.config.mjs b/apps/web-antd/postcss.config.mjs new file mode 100644 index 0000000..3d80704 --- /dev/null +++ b/apps/web-antd/postcss.config.mjs @@ -0,0 +1 @@ +export { default } from '@vben/tailwind-config/postcss'; diff --git a/apps/web-antd/public/favicon.ico b/apps/web-antd/public/favicon.ico new file mode 100644 index 0000000..b27828f Binary files /dev/null and b/apps/web-antd/public/favicon.ico differ diff --git a/apps/web-antd/public/logo.png b/apps/web-antd/public/logo.png new file mode 100644 index 0000000..b27828f Binary files /dev/null and b/apps/web-antd/public/logo.png differ diff --git a/apps/web-antd/public/tinymce/icons/default/icons.min.js b/apps/web-antd/public/tinymce/icons/default/icons.min.js new file mode 100644 index 0000000..5b761cb --- /dev/null +++ b/apps/web-antd/public/tinymce/icons/default/icons.min.js @@ -0,0 +1 @@ +tinymce.IconManager.add("default",{icons:{"accessibility-check":'',"accordion-toggle":'',accordion:'',"action-next":'',"action-prev":'',addtag:'',"ai-prompt":'',ai:'',"align-center":'',"align-justify":'',"align-left":'',"align-none":'',"align-right":'',"arrow-left":'',"arrow-right":'',bold:'',bookmark:'',"border-style":'',"border-width":'',brightness:'',browse:'',cancel:'',"cell-background-color":'',"cell-border-color":'',"change-case":'',"character-count":'',"checklist-rtl":'',checklist:'',checkmark:'',"chevron-down":'',"chevron-left":'',"chevron-right":'',"chevron-up":'',close:'',"code-sample":'',"color-levels":'',"color-picker":'',"color-swatch-remove-color":'',"color-swatch":'',"comment-add":'',comment:'',contrast:'',copy:'',crop:'',"cut-column":'',"cut-row":'',cut:'',"document-properties":'',drag:'',"duplicate-column":'',"duplicate-row":'',duplicate:'',"edit-block":'',"edit-image":'',"embed-page":'',embed:'',emoji:'',export:'',fill:'',"flip-horizontally":'',"flip-vertically":'',footnote:'',"format-painter":'',format:'',fullscreen:'',gallery:'',gamma:'',help:'',"highlight-bg-color":'',home:'',"horizontal-rule":'',"image-options":'',image:'',indent:'',info:'',"insert-character":'',"insert-time":'',invert:'',italic:'',language:'',"line-height":'',line:'',link:'',"list-bull-circle":'',"list-bull-default":'',"list-bull-square":'',"list-num-default-rtl":'',"list-num-default":'',"list-num-lower-alpha-rtl":'',"list-num-lower-alpha":'',"list-num-lower-greek-rtl":'',"list-num-lower-greek":'',"list-num-lower-roman-rtl":'',"list-num-lower-roman":'',"list-num-upper-alpha-rtl":'',"list-num-upper-alpha":'',"list-num-upper-roman-rtl":'',"list-num-upper-roman":'',lock:'',ltr:'',"math-equation":'',minus:'',"more-drawer":'',"new-document":'',"new-tab":'',"non-breaking":'',notice:'',"ordered-list-rtl":'',"ordered-list":'',orientation:'',outdent:'',"page-break":'',paragraph:'',"paste-column-after":'',"paste-column-before":'',"paste-row-after":'',"paste-row-before":'',"paste-text":'',paste:'',"permanent-pen":'',plus:'',preferences:'',preview:'',print:'',quote:'',redo:'',reload:'',"remove-formatting":'',remove:'',"resize-handle":'',resize:'',"restore-draft":'',"revision-history":'',"rotate-left":'',"rotate-right":'',rtl:'',save:'',search:'',"select-all":'',selected:'',send:'',settings:'',sharpen:'',sourcecode:'',"spell-check":'',"strike-through":'',subscript:'',superscript:'',"table-caption":'',"table-cell-classes":'',"table-cell-properties":'',"table-cell-select-all":'',"table-cell-select-inner":'',"table-classes":'',"table-delete-column":'',"table-delete-row":'',"table-delete-table":'',"table-insert-column-after":'',"table-insert-column-before":'',"table-insert-row-above":'',"table-insert-row-after":'',"table-left-header":'',"table-merge-cells":'',"table-row-numbering-rtl":'',"table-row-numbering":'',"table-row-properties":'',"table-split-cells":'',"table-top-header":'',table:'',"template-add":'',template:'',"temporary-placeholder":'',"text-color":'',"text-size-decrease":'',"text-size-increase":'',toc:'',translate:'',typography:'',underline:'',undo:'',unlink:'',unlock:'',"unordered-list":'',unselected:'',upload:'',user:'',"vertical-align":'',visualblocks:'',visualchars:'',warning:'',"zoom-in":'',"zoom-out":'',"export-pdf":'',"export-word":'',"import-word":''}}); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/langs/README.md b/apps/web-antd/public/tinymce/langs/README.md new file mode 100644 index 0000000..cd93d8c --- /dev/null +++ b/apps/web-antd/public/tinymce/langs/README.md @@ -0,0 +1,3 @@ +This is where language files should be placed. + +Please DO NOT translate these directly, use this service instead: https://crowdin.com/project/tinymce diff --git a/apps/web-antd/public/tinymce/langs/zh_CN.js b/apps/web-antd/public/tinymce/langs/zh_CN.js new file mode 100644 index 0000000..ff4b559 --- /dev/null +++ b/apps/web-antd/public/tinymce/langs/zh_CN.js @@ -0,0 +1 @@ +tinymce.addI18n("zh_CN",{"#":"#","Accessibility":"\u8f85\u52a9\u529f\u80fd","Accordion":"","Accordion body...":"","Accordion summary...":"","Action":"\u52a8\u4f5c","Activity":"\u6d3b\u52a8","Address":"\u5730\u5740","Advanced":"\u9ad8\u7ea7","Align":"\u5bf9\u9f50","Align center":"\u5c45\u4e2d\u5bf9\u9f50","Align left":"\u5de6\u5bf9\u9f50","Align right":"\u53f3\u5bf9\u9f50","Alignment":"\u5bf9\u9f50","Alignment {0}":"","All":"\u5168\u90e8","Alternative description":"\u66ff\u4ee3\u63cf\u8ff0","Alternative source":"\u955c\u50cf","Alternative source URL":"\u66ff\u4ee3\u6765\u6e90\u7f51\u5740","Anchor":"\u951a\u70b9","Anchor...":"\u951a\u70b9...","Anchors":"\u951a\u70b9","Animals and Nature":"\u52a8\u7269\u548c\u81ea\u7136","Arrows":"\u7bad\u5934","B":"B","Background color":"\u80cc\u666f\u989c\u8272","Background color {0}":"","Black":"\u9ed1\u8272","Block":"\u5757","Block {0}":"","Blockquote":"\u5f15\u6587\u533a\u5757","Blocks":"\u6837\u5f0f","Blue":"\u84dd\u8272","Blue component":"\u767d\u8272\u90e8\u5206","Body":"\u8868\u4f53","Bold":"\u7c97\u4f53","Border":"\u6846\u7ebf","Border color":"\u6846\u7ebf\u989c\u8272","Border style":"\u8fb9\u6846\u6837\u5f0f","Border width":"\u8fb9\u6846\u5bbd\u5ea6","Bottom":"\u4e0b\u65b9\u5bf9\u9f50","Browse files":"","Browse for an image":"\u6d4f\u89c8\u56fe\u50cf","Browse links":"","Bullet list":"\u65e0\u5e8f\u5217\u8868","Cancel":"\u53d6\u6d88","Caption":"\u6807\u9898","Cell":"\u5355\u5143\u683c","Cell padding":"\u5355\u5143\u683c\u5185\u8fb9\u8ddd","Cell properties":"\u5355\u5143\u683c\u5c5e\u6027","Cell spacing":"\u5355\u5143\u683c\u5916\u95f4\u8ddd","Cell styles":"\u5355\u5143\u683c\u6837\u5f0f","Cell type":"\u50a8\u5b58\u683c\u522b","Center":"\u5c45\u4e2d","Characters":"\u5b57\u7b26","Characters (no spaces)":"\u5b57\u7b26(\u65e0\u7a7a\u683c)","Circle":"\u7a7a\u5fc3\u5706","Class":"\u7c7b\u578b","Clear formatting":"\u6e05\u9664\u683c\u5f0f","Close":"\u5173\u95ed","Code":"\u4ee3\u7801","Code sample...":"\u793a\u4f8b\u4ee3\u7801...","Code view":"\u4ee3\u7801\u89c6\u56fe","Color Picker":"\u9009\u8272\u5668","Color swatch":"\u989c\u8272\u6837\u672c","Cols":"\u5217","Column":"\u5217","Column clipboard actions":"\u5217\u526a\u8d34\u677f\u64cd\u4f5c","Column group":"\u5217\u7ec4","Column header":"\u5217\u6807\u9898","Constrain proportions":"\u4fdd\u6301\u6bd4\u4f8b","Copy":"\u590d\u5236","Copy column":"\u590d\u5236\u5217","Copy row":"\u590d\u5236\u884c","Could not find the specified string.":"\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9\u3002","Could not load emojis":"\u65e0\u6cd5\u52a0\u8f7dEmojis","Count":"\u8ba1\u6570","Currency":"\u8d27\u5e01","Current window":"\u5f53\u524d\u7a97\u53e3","Custom color":"\u81ea\u5b9a\u4e49\u989c\u8272","Custom...":"\u81ea\u5b9a\u4e49......","Cut":"\u526a\u5207","Cut column":"\u526a\u5207\u5217","Cut row":"\u526a\u5207\u884c","Dark Blue":"\u6df1\u84dd\u8272","Dark Gray":"\u6df1\u7070\u8272","Dark Green":"\u6df1\u7eff\u8272","Dark Orange":"\u6df1\u6a59\u8272","Dark Purple":"\u6df1\u7d2b\u8272","Dark Red":"\u6df1\u7ea2\u8272","Dark Turquoise":"\u6df1\u84dd\u7eff\u8272","Dark Yellow":"\u6697\u9ec4\u8272","Dashed":"\u865a\u7ebf","Date/time":"\u65e5\u671f/\u65f6\u95f4","Decrease indent":"\u51cf\u5c11\u7f29\u8fdb","Default":"\u9884\u8bbe","Delete accordion":"","Delete column":"\u5220\u9664\u5217","Delete row":"\u5220\u9664\u884c","Delete table":"\u5220\u9664\u8868\u683c","Dimensions":"\u5c3a\u5bf8","Disc":"\u5b9e\u5fc3\u5706","Div":"Div","Document":"\u6587\u6863","Dotted":"\u865a\u7ebf","Double":"\u53cc\u7cbe\u5ea6","Drop an image here":"\u62d6\u653e\u4e00\u5f20\u56fe\u50cf\u81f3\u6b64","Dropped file type is not supported":"\u6b64\u6587\u4ef6\u7c7b\u578b\u4e0d\u652f\u6301\u62d6\u653e","Edit":"\u7f16\u8f91","Embed":"\u5185\u5d4c","Emojis":"Emojis","Emojis...":"Emojis...","Error":"\u9519\u8bef","Error: Form submit field collision.":"\u9519\u8bef: \u8868\u5355\u63d0\u4ea4\u5b57\u6bb5\u51b2\u7a81\u3002","Error: No form element found.":"\u9519\u8bef: \u6ca1\u6709\u8868\u5355\u63a7\u4ef6\u3002","Extended Latin":"\u62c9\u4e01\u8bed\u6269\u5145","Failed to initialize plugin: {0}":"\u63d2\u4ef6\u521d\u59cb\u5316\u5931\u8d25: {0}","Failed to load plugin url: {0}":"\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25 \u94fe\u63a5: {0}","Failed to load plugin: {0} from url {1}":"\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25: {0} \u6765\u81ea\u94fe\u63a5 {1}","Failed to upload image: {0}":"\u56fe\u7247\u4e0a\u4f20\u5931\u8d25: {0}","File":"\u6587\u4ef6","Find":"\u5bfb\u627e","Find (if searchreplace plugin activated)":"\u67e5\u627e(\u5982\u679c\u67e5\u627e\u66ff\u6362\u63d2\u4ef6\u5df2\u6fc0\u6d3b)","Find and Replace":"\u67e5\u627e\u548c\u66ff\u6362","Find and replace...":"\u67e5\u627e\u5e76\u66ff\u6362...","Find in selection":"\u5728\u9009\u533a\u4e2d\u67e5\u627e","Find whole words only":"\u5168\u5b57\u5339\u914d","Flags":"\u65d7\u5e1c","Focus to contextual toolbar":"\u79fb\u52a8\u7126\u70b9\u5230\u4e0a\u4e0b\u6587\u83dc\u5355","Focus to element path":"\u79fb\u52a8\u7126\u70b9\u5230\u5143\u7d20\u8def\u5f84","Focus to menubar":"\u79fb\u52a8\u7126\u70b9\u5230\u83dc\u5355\u680f","Focus to toolbar":"\u79fb\u52a8\u7126\u70b9\u5230\u5de5\u5177\u680f","Font":"\u5b57\u4f53","Font size {0}":"","Font sizes":"\u5b57\u4f53\u5927\u5c0f","Font {0}":"","Fonts":"\u5b57\u4f53","Food and Drink":"\u98df\u7269\u548c\u996e\u54c1","Footer":"\u8868\u5c3e","Format":"\u683c\u5f0f","Format {0}":"","Formats":"\u683c\u5f0f","Fullscreen":"\u5168\u5c4f","G":"G","General":"\u4e00\u822c","Gray":"\u7070\u8272","Green":"\u7eff\u8272","Green component":"\u7eff\u8272\u90e8\u5206","Groove":"\u51f9\u69fd","Handy Shortcuts":"\u5feb\u6377\u952e","Header":"\u8868\u5934","Header cell":"\u8868\u5934\u5355\u5143\u683c","Heading 1":"\u4e00\u7ea7\u6807\u9898","Heading 2":"\u4e8c\u7ea7\u6807\u9898","Heading 3":"\u4e09\u7ea7\u6807\u9898","Heading 4":"\u56db\u7ea7\u6807\u9898","Heading 5":"\u4e94\u7ea7\u6807\u9898","Heading 6":"\u516d\u7ea7\u6807\u9898","Headings":"\u6807\u9898","Height":"\u9ad8\u5ea6","Help":"\u5e2e\u52a9","Hex color code":"\u5341\u516d\u8fdb\u5236\u989c\u8272\u4ee3\u7801","Hidden":"\u9690\u85cf","Horizontal align":"\u6c34\u5e73\u5bf9\u9f50","Horizontal line":"\u6c34\u5e73\u5206\u5272\u7ebf","Horizontal space":"\u6c34\u5e73\u95f4\u8ddd","ID":"ID","ID should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.":"ID\u5e94\u8be5\u4ee5\u82f1\u6587\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u9762\u53ea\u80fd\u6709\u82f1\u6587\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002","Image is decorative":"\u56fe\u50cf\u662f\u88c5\u9970\u6027\u7684","Image list":"\u56fe\u7247\u6e05\u5355","Image title":"\u56fe\u7247\u6807\u9898","Image...":"\u56fe\u7247...","ImageProxy HTTP error: Could not find Image Proxy":"\u56fe\u7247\u4ee3\u7406\u8bf7\u6c42\u9519\u8bef\uff1a\u65e0\u6cd5\u627e\u5230\u56fe\u7247\u4ee3\u7406","ImageProxy HTTP error: Incorrect Image Proxy URL":"\u56fe\u7247\u4ee3\u7406\u8bf7\u6c42\u9519\u8bef\uff1a\u56fe\u7247\u4ee3\u7406\u5730\u5740\u9519\u8bef","ImageProxy HTTP error: Rejected request":"\u56fe\u7247\u4ee3\u7406\u8bf7\u6c42\u9519\u8bef\uff1a\u8bf7\u6c42\u88ab\u62d2\u7edd","ImageProxy HTTP error: Unknown ImageProxy error":"\u56fe\u7247\u4ee3\u7406\u8bf7\u6c42\u9519\u8bef\uff1a\u672a\u77e5\u7684\u56fe\u7247\u4ee3\u7406\u9519\u8bef","Increase indent":"\u589e\u52a0\u7f29\u8fdb","Inline":"\u6587\u672c","Insert":"\u63d2\u5165","Insert Template":"\u63d2\u5165\u6a21\u677f","Insert accordion":"","Insert column after":"\u5728\u53f3\u4fa7\u63d2\u5165\u5217","Insert column before":"\u5728\u5de6\u4fa7\u63d2\u5165\u5217","Insert date/time":"\u63d2\u5165\u65e5\u671f/\u65f6\u95f4","Insert image":"\u63d2\u5165\u56fe\u7247","Insert link (if link plugin activated)":"\u63d2\u5165\u94fe\u63a5 (\u5982\u679c\u94fe\u63a5\u63d2\u4ef6\u5df2\u6fc0\u6d3b)","Insert row after":"\u5728\u4e0b\u65b9\u63d2\u5165\u884c","Insert row before":"\u5728\u4e0a\u65b9\u63d2\u5165\u884c","Insert table":"\u63d2\u5165\u8868\u683c","Insert template...":"\u63d2\u5165\u6a21\u677f...","Insert video":"\u63d2\u5165\u89c6\u9891","Insert/Edit code sample":"\u63d2\u5165/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b","Insert/edit image":"\u63d2\u5165/\u7f16\u8f91\u56fe\u7247","Insert/edit link":"\u63d2\u5165/\u7f16\u8f91\u94fe\u63a5","Insert/edit media":"\u63d2\u5165/\u7f16\u8f91\u5a92\u4f53","Insert/edit video":"\u63d2\u5165/\u7f16\u8f91\u89c6\u9891","Inset":"\u5d4c\u5165","Invalid hex color code: {0}":"\u5341\u516d\u8fdb\u5236\u989c\u8272\u4ee3\u7801\u65e0\u6548\uff1a {0}","Invalid input":"\u65e0\u6548\u8f93\u5165","Italic":"\u659c\u4f53","Justify":"\u4e24\u7aef\u5bf9\u9f50","Keyboard Navigation":"\u952e\u76d8\u6307\u5f15","Language":"\u8bed\u8a00","Learn more...":"\u4e86\u89e3\u66f4\u591a...","Left":"\u5de6","Left to right":"\u7531\u5de6\u5230\u53f3","Light Blue":"\u6d45\u84dd\u8272","Light Gray":"\u6d45\u7070\u8272","Light Green":"\u6d45\u7eff\u8272","Light Purple":"\u6d45\u7d2b\u8272","Light Red":"\u6d45\u7ea2\u8272","Light Yellow":"\u6d45\u9ec4\u8272","Line height":"\u884c\u9ad8","Link list":"\u94fe\u63a5\u6e05\u5355","Link...":"\u94fe\u63a5...","List Properties":"\u5217\u8868\u5c5e\u6027","List properties...":"\u6807\u9898\u5b57\u4f53\u5c5e\u6027","Loading emojis...":"\u6b63\u5728\u52a0\u8f7dEmojis...","Loading...":"\u52a0\u8f7d\u4e2d...","Lower Alpha":"\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd","Lower Greek":"\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd","Lower Roman":"\u5c0f\u5199\u7f57\u9a6c\u6570\u5b57","Match case":"\u5927\u5c0f\u5199\u5339\u914d","Mathematical":"\u6570\u5b66","Media poster (Image URL)":"\u5c01\u9762(\u56fe\u7247\u5730\u5740)","Media...":"\u591a\u5a92\u4f53...","Medium Blue":"\u4e2d\u84dd\u8272","Medium Gray":"\u4e2d\u7070\u8272","Medium Purple":"\u4e2d\u7d2b\u8272","Merge cells":"\u5408\u5e76\u5355\u5143\u683c","Middle":"\u5c45\u4e2d\u5bf9\u9f50","Midnight Blue":"\u6df1\u84dd\u8272","More...":"\u66f4\u591a...","Name":"\u540d\u79f0","Navy Blue":"\u6d77\u519b\u84dd","New document":"\u65b0\u5efa\u6587\u6863","New window":"\u65b0\u7a97\u53e3","Next":"\u4e0b\u4e00\u4e2a","No":"\u5426","No alignment":"\u672a\u5bf9\u9f50","No color":"\u65e0","Nonbreaking space":"\u4e0d\u95f4\u65ad\u7a7a\u683c","None":"\u65e0","Numbered list":"\u6709\u5e8f\u5217\u8868","OR":"\u6216","Objects":"\u7269\u4ef6","Ok":"\u786e\u5b9a","Open help dialog":"\u6253\u5f00\u5e2e\u52a9\u5bf9\u8bdd\u6846","Open link":"\u6253\u5f00\u94fe\u63a5","Open link in...":"\u94fe\u63a5\u6253\u5f00\u4f4d\u7f6e...","Open popup menu for split buttons":"\u6253\u5f00\u5f39\u51fa\u5f0f\u83dc\u5355\uff0c\u7528\u4e8e\u62c6\u5206\u6309\u94ae","Orange":"\u6a59\u8272","Outset":"\u5916\u7f6e","Page break":"\u5206\u9875\u7b26","Paragraph":"\u6bb5\u843d","Paste":"\u7c98\u8d34","Paste as text":"\u7c98\u8d34\u4e3a\u6587\u672c","Paste column after":"\u7c98\u8d34\u540e\u9762\u7684\u5217","Paste column before":"\u7c98\u8d34\u6b64\u5217\u524d","Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.":"\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u518d\u6b21\u70b9\u51fb\u53ef\u4ee5\u56de\u5230\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002","Paste or type a link":"\u7c98\u8d34\u6216\u8f93\u5165\u94fe\u63a5","Paste row after":"\u7c98\u8d34\u884c\u5230\u4e0b\u65b9","Paste row before":"\u7c98\u8d34\u884c\u5230\u4e0a\u65b9","Paste your embed code below:":"\u5c06\u5185\u5d4c\u4ee3\u7801\u7c98\u8d34\u5728\u4e0b\u9762:","People":"\u4eba\u7c7b","Plugins":"\u63d2\u4ef6","Plugins installed ({0}):":"\u5df2\u5b89\u88c5\u63d2\u4ef6 ({0}):","Powered by {0}":"\u7531{0}\u9a71\u52a8","Pre":"\u524d\u8a00","Preferences":"\u9996\u9009\u9879","Preformatted":"\u9884\u5148\u683c\u5f0f\u5316\u7684","Premium plugins:":"\u4f18\u79c0\u63d2\u4ef6\uff1a","Press the Up and Down arrow keys to resize the editor.":"","Press the arrow keys to resize the editor.":"","Press {0} for help":"","Preview":"\u9884\u89c8","Previous":"\u4e0a\u4e00\u4e2a","Print":"\u6253\u5370","Print...":"\u6253\u5370...","Purple":"\u7d2b\u8272","Quotations":"\u5f15\u7528","R":"R","Range 0 to 255":"\u8303\u56f40\u81f3255","Red":"\u7ea2\u8272","Red component":"\u7ea2\u8272\u90e8\u5206","Redo":"\u91cd\u505a","Remove":"\u79fb\u9664","Remove color":"\u79fb\u9664\u989c\u8272","Remove link":"\u79fb\u9664\u94fe\u63a5","Replace":"\u66ff\u6362","Replace all":"\u66ff\u6362\u5168\u90e8","Replace with":"\u66ff\u6362\u4e3a","Resize":"\u8c03\u6574\u5927\u5c0f","Restore last draft":"\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f","Reveal or hide additional toolbar items":"","Rich Text Area":"\u5bcc\u6587\u672c\u533a\u57df","Rich Text Area. Press ALT-0 for help.":"\u7f16\u8f91\u533a\u3002\u6309Alt+0\u952e\u6253\u5f00\u5e2e\u52a9\u3002","Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help":"\u7f16\u8f91\u533a\u3002\u6309ALT-F9\u6253\u5f00\u83dc\u5355\uff0c\u6309ALT-F10\u6253\u5f00\u5de5\u5177\u680f\uff0c\u6309ALT-0\u67e5\u770b\u5e2e\u52a9","Ridge":"\u6d77\u810a\u5ea7","Right":"\u53f3","Right to left":"\u7531\u53f3\u5230\u5de6","Row":"\u884c","Row clipboard actions":"\u884c\u526a\u8d34\u677f\u64cd\u4f5c","Row group":"\u884c\u7ec4","Row header":"\u884c\u5934","Row properties":"\u884c\u5c5e\u6027","Row type":"\u884c\u7c7b\u578b","Rows":"\u884c\u6570","Save":"\u4fdd\u5b58","Save (if save plugin activated)":"\u4fdd\u5b58(\u5982\u679c\u4fdd\u5b58\u63d2\u4ef6\u5df2\u6fc0\u6d3b)","Scope":"\u8303\u56f4","Search":"\u641c\u7d22","Select all":"\u5168\u9009","Select...":"\u9009\u62e9...","Selection":"\u9009\u62e9","Shortcut":"\u5feb\u6377\u65b9\u5f0f","Show blocks":"\u663e\u793a\u533a\u5757\u8fb9\u6846","Show caption":"\u663e\u793a\u6807\u9898","Show invisible characters":"\u663e\u793a\u4e0d\u53ef\u89c1\u5b57\u7b26","Size":"\u5b57\u53f7","Solid":"\u5b9e\u7ebf","Source":"\u6e90","Source code":"\u6e90\u4ee3\u7801","Special Character":"\u7279\u6b8a\u5b57\u7b26","Special character...":"\u7279\u6b8a\u5b57\u7b26...","Split cell":"\u62c6\u5206\u5355\u5143\u683c","Square":"\u5b9e\u5fc3\u65b9\u5757","Start list at number":"\u4ee5\u6570\u5b57\u5f00\u59cb\u5217\u8868","Strikethrough":"\u5220\u9664\u7ebf","Style":"\u6837\u5f0f","Subscript":"\u4e0b\u6807","Superscript":"\u4e0a\u6807","Switch to or from fullscreen mode":"\u5207\u6362\u5168\u5c4f\u6a21\u5f0f","Symbols":"\u7b26\u53f7","System Font":"\u7cfb\u7edf\u5b57\u4f53","Table":"\u8868\u683c","Table caption":"\u8868\u683c\u6807\u9898","Table properties":"\u8868\u683c\u5c5e\u6027","Table styles":"\u8868\u683c\u6837\u5f0f","Template":"\u6a21\u677f","Templates":"\u6a21\u677f","Text":"\u6587\u5b57","Text color":"\u6587\u672c\u989c\u8272","Text color {0}":"","Text to display":"\u8981\u663e\u793a\u7684\u6587\u672c","The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?":"\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u4e3a\u90ae\u4ef6\u5730\u5740\uff0c\u9700\u8981\u52a0\u4e0amailto: \u524d\u7f00\u5417\uff1f","The URL you entered seems to be an external link. Do you want to add the required http:// prefix?":"\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u5c5e\u4e8e\u5916\u90e8\u94fe\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp:// \u524d\u7f00\u5417\uff1f","The URL you entered seems to be an external link. Do you want to add the required https:// prefix?":"\u60a8\u8f93\u5165\u7684 URL \u4f3c\u4e4e\u662f\u4e00\u4e2a\u5916\u90e8\u94fe\u63a5\u3002\u60a8\u60f3\u6dfb\u52a0\u6240\u9700\u7684 https:// \u524d\u7f00\u5417\uff1f","Title":"\u6807\u9898","To open the popup, press Shift+Enter":"\u6309Shitf+Enter\u952e\u6253\u5f00\u5bf9\u8bdd\u6846","Toggle accordion":"","Tools":"\u5de5\u5177","Top":"\u4e0a\u65b9\u5bf9\u9f50","Travel and Places":"\u65c5\u6e38\u548c\u5730\u70b9","Turquoise":"\u9752\u7eff\u8272","Underline":"\u4e0b\u5212\u7ebf","Undo":"\u64a4\u9500","Upload":"\u4e0a\u4f20","Uploading image":"\u4e0a\u4f20\u56fe\u7247","Upper Alpha":"\u5927\u5199\u82f1\u6587\u5b57\u6bcd","Upper Roman":"\u5927\u5199\u7f57\u9a6c\u6570\u5b57","Url":"\u5730\u5740","User Defined":"\u81ea\u5b9a\u4e49","Valid":"\u6709\u6548","Version":"\u7248\u672c","Vertical align":"\u5782\u76f4\u5bf9\u9f50","Vertical space":"\u5782\u76f4\u95f4\u8ddd","View":"\u67e5\u770b","Visual aids":"\u7f51\u683c\u7ebf","Warn":"\u8b66\u544a","White":"\u767d\u8272","Width":"\u5bbd\u5ea6","Word count":"\u5b57\u6570","Words":"\u5355\u8bcd","Words: {0}":"\u5b57\u6570\uff1a{0}","Yellow":"\u9ec4\u8272","Yes":"\u662f","You are using {0}":"\u4f60\u6b63\u5728\u4f7f\u7528 {0}","You have unsaved changes are you sure you want to navigate away?":"\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f","Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.":"\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u6253\u5f00\u526a\u8d34\u677f\uff0c\u8bf7\u4f7f\u7528Ctrl+X/C/V\u7b49\u5feb\u6377\u952e\u3002","alignment":"\u5bf9\u9f50","austral sign":"\u6fb3\u5143\u7b26\u53f7","cedi sign":"\u585e\u5730\u7b26\u53f7","colon sign":"\u5192\u53f7","cruzeiro sign":"\u514b\u9c81\u8d5b\u7f57\u5e01\u7b26\u53f7","currency sign":"\u8d27\u5e01\u7b26\u53f7","dollar sign":"\u7f8e\u5143\u7b26\u53f7","dong sign":"\u8d8a\u5357\u76fe\u7b26\u53f7","drachma sign":"\u5fb7\u62c9\u514b\u9a6c\u7b26\u53f7","euro-currency sign":"\u6b27\u5143\u7b26\u53f7","example":"\u793a\u4f8b","formatting":"\u683c\u5f0f\u5316","french franc sign":"\u6cd5\u90ce\u7b26\u53f7","german penny symbol":"\u5fb7\u56fd\u4fbf\u58eb\u7b26\u53f7","guarani sign":"\u74dc\u62c9\u5c3c\u7b26\u53f7","history":"\u5386\u53f2","hryvnia sign":"\u683c\u91cc\u592b\u5c3c\u4e9a\u7b26\u53f7","indentation":"\u7f29\u8fdb","indian rupee sign":"\u5370\u5ea6\u5362\u6bd4","kip sign":"\u8001\u631d\u57fa\u666e\u7b26\u53f7","lira sign":"\u91cc\u62c9\u7b26\u53f7","livre tournois sign":"\u91cc\u5f17\u5f17\u5c14\u7b26\u53f7","manat sign":"\u9a6c\u7eb3\u7279\u7b26\u53f7","mill sign":"\u5bc6\u5c14\u7b26\u53f7","naira sign":"\u5948\u62c9\u7b26\u53f7","new sheqel sign":"\u65b0\u8c22\u514b\u5c14\u7b26\u53f7","nordic mark sign":"\u5317\u6b27\u9a6c\u514b","peseta sign":"\u6bd4\u585e\u5854\u7b26\u53f7","peso sign":"\u6bd4\u7d22\u7b26\u53f7","ruble sign":"\u5362\u5e03\u7b26\u53f7","rupee sign":"\u5362\u6bd4\u7b26\u53f7","spesmilo sign":"spesmilo\u7b26\u53f7","styles":"\u6837\u5f0f","tenge sign":"\u575a\u6208\u7b26\u53f7","tugrik sign":"\u56fe\u683c\u91cc\u514b\u7b26\u53f7","turkish lira sign":"\u571f\u8033\u5176\u91cc\u62c9","won sign":"\u97e9\u5143\u7b26\u53f7","yen character":"\u65e5\u5143\u5b57\u6837","yen/yuan character variant one":"\u5143\u5b57\u6837\uff08\u5927\u5199\uff09","yuan character":"\u4eba\u6c11\u5e01\u5143\u5b57\u6837","yuan character, in hong kong and taiwan":"\u5143\u5b57\u6837\uff08\u6e2f\u53f0\u5730\u533a\uff09","{0} characters":"{0} \u4e2a\u5b57\u7b26","{0} columns, {1} rows":"","{0} words":"{0} \u5b57"}); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/license.md b/apps/web-antd/public/tinymce/license.md new file mode 100644 index 0000000..70454a6 --- /dev/null +++ b/apps/web-antd/public/tinymce/license.md @@ -0,0 +1,6 @@ +# Software License Agreement + +**TinyMCE** – [](https://github.com/tinymce/tinymce) +Copyright (c) 2024, Ephox Corporation DBA Tiny Technologies, Inc. + +Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html). diff --git a/apps/web-antd/public/tinymce/models/dom/model.min.js b/apps/web-antd/public/tinymce/models/dom/model.min.js new file mode 100644 index 0000000..718c585 --- /dev/null +++ b/apps/web-antd/public/tinymce/models/dom/model.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.ModelManager");const t=e=>t=>(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(o=n=e,(r=String).prototype.isPrototypeOf(o)||(null===(s=n.constructor)||void 0===s?void 0:s.name)===r.name)?"string":t;var o,n,r,s})(t)===e,o=e=>t=>typeof t===e,n=e=>t=>e===t,r=t("string"),s=t("object"),l=t("array"),a=n(null),c=o("boolean"),i=n(void 0),m=e=>!(e=>null==e)(e),d=o("function"),u=o("number"),f=()=>{},g=e=>()=>e,h=e=>e,p=(e,t)=>e===t;function b(e,...t){return(...o)=>{const n=t.concat(o);return e.apply(null,n)}}const w=e=>t=>!e(t),v=e=>e(),y=g(!1),x=g(!0);class C{constructor(e,t){this.tag=e,this.value=t}static some(e){return new C(!0,e)}static none(){return C.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?C.some(e(this.value)):C.none()}bind(e){return this.tag?e(this.value):C.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:C.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return m(e)?C.some(e):C.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}C.singletonNone=new C(!1);const S=Array.prototype.slice,T=Array.prototype.indexOf,R=Array.prototype.push,D=(e,t)=>{return o=e,n=t,T.call(o,n)>-1;var o,n},O=(e,t)=>{for(let o=0,n=e.length;o{const o=[];for(let n=0;n{const o=e.length,n=new Array(o);for(let r=0;r{for(let o=0,n=e.length;o{const o=[],n=[];for(let r=0,s=e.length;r{const o=[];for(let n=0,r=e.length;n(((e,t)=>{for(let o=e.length-1;o>=0;o--)t(e[o],o)})(e,((e,n)=>{o=t(o,e,n)})),o),A=(e,t,o)=>(N(e,((e,n)=>{o=t(o,e,n)})),o),L=(e,t)=>((e,t,o)=>{for(let n=0,r=e.length;n{for(let o=0,n=e.length;o{const t=[];for(let o=0,n=e.length;oM(E(e,t)),P=(e,t)=>{for(let o=0,n=e.length;o{const o={};for(let n=0,r=e.length;nt>=0&&tF(e,0),$=e=>F(e,e.length-1),V=(e,t)=>{for(let o=0;o{const o=q(e);for(let n=0,r=o.length;nY(e,((e,o)=>({k:o,v:t(e,o)}))),Y=(e,t)=>{const o={};return G(e,((e,n)=>{const r=t(e,n);o[r.k]=r.v})),o},J=(e,t)=>{const o=[];return G(e,((e,n)=>{o.push(t(e,n))})),o},Q=e=>J(e,h),X=(e,t)=>U.call(e,t),Z="undefined"!=typeof window?window:Function("return this;")(),ee=(e,t)=>((e,t)=>{let o=null!=t?t:Z;for(let t=0;t{const t=ee("ownerDocument.defaultView",e);return s(e)&&((e=>((e,t)=>{const o=((e,t)=>ee(e,t))(e,t);if(null==o)throw new Error(e+" not available on this browser");return o})("HTMLElement",e))(t).prototype.isPrototypeOf(e)||/^HTML\w*Element$/.test(te(e).constructor.name))},ne=e=>e.dom.nodeName.toLowerCase(),re=e=>e.dom.nodeType,se=e=>t=>re(t)===e,le=e=>8===re(e)||"#comment"===ne(e),ae=e=>ce(e)&&oe(e.dom),ce=se(1),ie=se(3),me=se(9),de=se(11),ue=e=>t=>ce(t)&&ne(t)===e,fe=(e,t,o)=>{if(!(r(o)||c(o)||u(o)))throw console.error("Invalid call to Attribute.set. Key ",t,":: Value ",o,":: Element ",e),new Error("Attribute value was not simple");e.setAttribute(t,o+"")},ge=(e,t,o)=>{fe(e.dom,t,o)},he=(e,t)=>{const o=e.dom;G(t,((e,t)=>{fe(o,t,e)}))},pe=(e,t)=>{const o=e.dom.getAttribute(t);return null===o?void 0:o},be=(e,t)=>C.from(pe(e,t)),we=(e,t)=>{e.dom.removeAttribute(t)},ve=e=>A(e.dom.attributes,((e,t)=>(e[t.name]=t.value,e)),{}),ye=e=>{if(null==e)throw new Error("Node cannot be null or undefined");return{dom:e}},xe={fromHtml:(e,t)=>{const o=(t||document).createElement("div");if(o.innerHTML=e,!o.hasChildNodes()||o.childNodes.length>1){const t="HTML does not have a single root node";throw console.error(t,e),new Error(t)}return ye(o.childNodes[0])},fromTag:(e,t)=>{const o=(t||document).createElement(e);return ye(o)},fromText:(e,t)=>{const o=(t||document).createTextNode(e);return ye(o)},fromDom:ye,fromPoint:(e,t,o)=>C.from(e.dom.elementFromPoint(t,o)).map(ye)},Ce=(e,t)=>{const o=e.dom;if(1!==o.nodeType)return!1;{const e=o;if(void 0!==e.matches)return e.matches(t);if(void 0!==e.msMatchesSelector)return e.msMatchesSelector(t);if(void 0!==e.webkitMatchesSelector)return e.webkitMatchesSelector(t);if(void 0!==e.mozMatchesSelector)return e.mozMatchesSelector(t);throw new Error("Browser lacks native selectors")}},Se=e=>1!==e.nodeType&&9!==e.nodeType&&11!==e.nodeType||0===e.childElementCount,Te=(e,t)=>{const o=void 0===t?document:t.dom;return Se(o)?C.none():C.from(o.querySelector(e)).map(xe.fromDom)},Re=(e,t)=>e.dom===t.dom,De=(e,t)=>{const o=e.dom,n=t.dom;return o!==n&&o.contains(n)},Oe=Ce,ke=e=>xe.fromDom(e.dom.ownerDocument),Ee=e=>me(e)?e:ke(e),Ne=e=>C.from(e.dom.parentNode).map(xe.fromDom),Be=e=>C.from(e.dom.parentElement).map(xe.fromDom),_e=(e,t)=>{const o=d(t)?t:y;let n=e.dom;const r=[];for(;null!==n.parentNode&&void 0!==n.parentNode;){const e=n.parentNode,t=xe.fromDom(e);if(r.push(t),!0===o(t))break;n=e}return r},ze=e=>C.from(e.dom.previousSibling).map(xe.fromDom),Ae=e=>C.from(e.dom.nextSibling).map(xe.fromDom),Le=e=>E(e.dom.childNodes,xe.fromDom),We=(e,t)=>{const o=e.dom.childNodes;return C.from(o[t]).map(xe.fromDom)},Me=(e,t)=>{Ne(e).each((o=>{o.dom.insertBefore(t.dom,e.dom)}))},je=(e,t)=>{Ae(e).fold((()=>{Ne(e).each((e=>{Ie(e,t)}))}),(e=>{Me(e,t)}))},Pe=(e,t)=>{const o=(e=>We(e,0))(e);o.fold((()=>{Ie(e,t)}),(o=>{e.dom.insertBefore(t.dom,o.dom)}))},Ie=(e,t)=>{e.dom.appendChild(t.dom)},Fe=(e,t)=>{Me(e,t),Ie(t,e)},He=(e,t)=>{N(t,((o,n)=>{const r=0===n?e:t[n-1];je(r,o)}))},$e=(e,t)=>{N(t,(t=>{Ie(e,t)}))},Ve=e=>{e.dom.textContent="",N(Le(e),(e=>{qe(e)}))},qe=e=>{const t=e.dom;null!==t.parentNode&&t.parentNode.removeChild(t)},Ue=e=>{const t=Le(e);t.length>0&&He(e,t),qe(e)},Ge=(e,t)=>xe.fromDom(e.dom.cloneNode(t)),Ke=e=>Ge(e,!1),Ye=e=>Ge(e,!0),Je=(e,t)=>{const o=xe.fromTag(t),n=ve(e);return he(o,n),o},Qe=["tfoot","thead","tbody","colgroup"],Xe=(e,t,o)=>({element:e,rowspan:t,colspan:o}),Ze=(e,t,o)=>({element:e,cells:t,section:o}),et=(e,t,o)=>({element:e,isNew:t,isLocked:o}),tt=(e,t,o,n)=>({element:e,cells:t,section:o,isNew:n}),ot=d(Element.prototype.attachShadow)&&d(Node.prototype.getRootNode),nt=g(ot),rt=ot?e=>xe.fromDom(e.dom.getRootNode()):Ee,st=e=>xe.fromDom(e.dom.host),lt=e=>{const t=ie(e)?e.dom.parentNode:e.dom;if(null==t||null===t.ownerDocument)return!1;const o=t.ownerDocument;return(e=>{const t=rt(e);return de(o=t)&&m(o.dom.host)?C.some(t):C.none();var o})(xe.fromDom(t)).fold((()=>o.body.contains(t)),(n=lt,r=st,e=>n(r(e))));var n,r},at=e=>{const t=e.dom.body;if(null==t)throw new Error("Body is not available yet");return xe.fromDom(t)},ct=(e,t)=>{let o=[];return N(Le(e),(e=>{t(e)&&(o=o.concat([e])),o=o.concat(ct(e,t))})),o},it=(e,t,o)=>((e,o,n)=>_(_e(e,n),(e=>Ce(e,t))))(e,0,o),mt=(e,t)=>((e,o)=>_(Le(e),(e=>Ce(e,t))))(e),dt=(e,t)=>((e,t)=>{const o=void 0===t?document:t.dom;return Se(o)?[]:E(o.querySelectorAll(e),xe.fromDom)})(t,e);var ut=(e,t,o,n,r)=>e(o,n)?C.some(o):d(r)&&r(o)?C.none():t(o,n,r);const ft=(e,t,o)=>{let n=e.dom;const r=d(o)?o:y;for(;n.parentNode;){n=n.parentNode;const e=xe.fromDom(n);if(t(e))return C.some(e);if(r(e))break}return C.none()},gt=(e,t,o)=>ut(((e,t)=>t(e)),ft,e,t,o),ht=(e,t,o)=>ft(e,(e=>Ce(e,t)),o),pt=(e,t)=>((e,o)=>L(e.dom.childNodes,(e=>{return o=xe.fromDom(e),Ce(o,t);var o})).map(xe.fromDom))(e),bt=(e,t)=>Te(t,e),wt=(e,t,o)=>ut(((e,t)=>Ce(e,t)),ht,e,t,o),vt=(e,t,o=p)=>e.exists((e=>o(e,t))),yt=e=>{const t=[],o=e=>{t.push(e)};for(let t=0;te?C.some(t):C.none(),Ct=(e,t,o)=>""===t||e.length>=t.length&&e.substr(o,o+t.length)===t,St=(e,t,o=0,n)=>{const r=e.indexOf(t,o);return-1!==r&&(!!i(n)||r+t.length<=n)},Tt=(e,t)=>Ct(e,t,0),Rt=(e,t)=>Ct(e,t,e.length-t.length),Dt=(e=>t=>t.replace(e,""))(/^\s+|\s+$/g),Ot=e=>e.length>0,kt=e=>void 0!==e.style&&d(e.style.getPropertyValue),Et=(e,t,o)=>{if(!r(o))throw console.error("Invalid call to CSS.set. Property ",t,":: Value ",o,":: Element ",e),new Error("CSS value must be a string: "+o);kt(e)&&e.style.setProperty(t,o)},Nt=(e,t,o)=>{const n=e.dom;Et(n,t,o)},Bt=(e,t)=>{const o=e.dom;G(t,((e,t)=>{Et(o,t,e)}))},_t=(e,t)=>{const o=e.dom,n=window.getComputedStyle(o).getPropertyValue(t);return""!==n||lt(e)?n:zt(o,t)},zt=(e,t)=>kt(e)?e.style.getPropertyValue(t):"",At=(e,t)=>{const o=e.dom,n=zt(o,t);return C.from(n).filter((e=>e.length>0))},Lt=(e,t)=>{((e,t)=>{kt(e)&&e.style.removeProperty(t)})(e.dom,t),vt(be(e,"style").map(Dt),"")&&we(e,"style")},Wt=(e,t,o=0)=>be(e,t).map((e=>parseInt(e,10))).getOr(o),Mt=(e,t)=>Wt(e,t,1),jt=e=>ue("col")(e)?Wt(e,"span",1)>1:Mt(e,"colspan")>1,Pt=(e,t)=>parseInt(_t(e,t),10),It=g(10),Ft=g(10),Ht=(e,t)=>$t(e,t,x),$t=(e,t,o)=>j(Le(e),(e=>Ce(e,t)?o(e)?[e]:[]:$t(e,t,o))),Vt=(e,t)=>((e,t,o=y)=>o(t)?C.none():D(e,ne(t))?C.some(t):ht(t,e.join(","),(e=>Ce(e,"table")||o(e))))(["td","th"],e,t),qt=e=>Ht(e,"th,td"),Ut=e=>Ce(e,"colgroup")?mt(e,"col"):j(Yt(e),(e=>mt(e,"col"))),Gt=(e,t)=>wt(e,"table",t),Kt=e=>Ht(e,"tr"),Yt=e=>Gt(e).fold(g([]),(e=>mt(e,"colgroup"))),Jt=(e,t)=>E(e,(e=>{if("colgroup"===ne(e)){const t=E(Ut(e),(e=>{const t=Wt(e,"span",1);return Xe(e,1,t)}));return Ze(e,t,"colgroup")}{const o=E(qt(e),(e=>{const t=Wt(e,"rowspan",1),o=Wt(e,"colspan",1);return Xe(e,t,o)}));return Ze(e,o,t(e))}})),Qt=e=>Ne(e).map((e=>{const t=ne(e);return(e=>D(Qe,e))(t)?t:"tbody"})).getOr("tbody"),Xt=e=>{const t=Kt(e),o=[...Yt(e),...t];return Jt(o,Qt)},Zt=e=>{let t,o=!1;return(...n)=>(o||(o=!0,t=e.apply(null,n)),t)},eo=()=>to(0,0),to=(e,t)=>({major:e,minor:t}),oo={nu:to,detect:(e,t)=>{const o=String(t).toLowerCase();return 0===e.length?eo():((e,t)=>{const o=((e,t)=>{for(let o=0;oNumber(t.replace(o,"$"+e));return to(n(1),n(2))})(e,o)},unknown:eo},no=(e,t)=>{const o=String(t).toLowerCase();return L(e,(e=>e.search(o)))},ro=/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,so=e=>t=>St(t,e),lo=[{name:"Edge",versionRegexes:[/.*?edge\/ ?([0-9]+)\.([0-9]+)$/],search:e=>St(e,"edge/")&&St(e,"chrome")&&St(e,"safari")&&St(e,"applewebkit")},{name:"Chromium",brand:"Chromium",versionRegexes:[/.*?chrome\/([0-9]+)\.([0-9]+).*/,ro],search:e=>St(e,"chrome")&&!St(e,"chromeframe")},{name:"IE",versionRegexes:[/.*?msie\ ?([0-9]+)\.([0-9]+).*/,/.*?rv:([0-9]+)\.([0-9]+).*/],search:e=>St(e,"msie")||St(e,"trident")},{name:"Opera",versionRegexes:[ro,/.*?opera\/([0-9]+)\.([0-9]+).*/],search:so("opera")},{name:"Firefox",versionRegexes:[/.*?firefox\/\ ?([0-9]+)\.([0-9]+).*/],search:so("firefox")},{name:"Safari",versionRegexes:[ro,/.*?cpu os ([0-9]+)_([0-9]+).*/],search:e=>(St(e,"safari")||St(e,"mobile/"))&&St(e,"applewebkit")}],ao=[{name:"Windows",search:so("win"),versionRegexes:[/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/]},{name:"iOS",search:e=>St(e,"iphone")||St(e,"ipad"),versionRegexes:[/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,/.*cpu os ([0-9]+)_([0-9]+).*/,/.*cpu iphone os ([0-9]+)_([0-9]+).*/]},{name:"Android",search:so("android"),versionRegexes:[/.*?android\ ?([0-9]+)\.([0-9]+).*/]},{name:"macOS",search:so("mac os x"),versionRegexes:[/.*?mac\ os\ x\ ?([0-9]+)_([0-9]+).*/]},{name:"Linux",search:so("linux"),versionRegexes:[]},{name:"Solaris",search:so("sunos"),versionRegexes:[]},{name:"FreeBSD",search:so("freebsd"),versionRegexes:[]},{name:"ChromeOS",search:so("cros"),versionRegexes:[/.*?chrome\/([0-9]+)\.([0-9]+).*/]}],co={browsers:g(lo),oses:g(ao)},io="Edge",mo="Chromium",uo="Opera",fo="Firefox",go="Safari",ho=e=>{const t=e.current,o=e.version,n=e=>()=>t===e;return{current:t,version:o,isEdge:n(io),isChromium:n(mo),isIE:n("IE"),isOpera:n(uo),isFirefox:n(fo),isSafari:n(go)}},po=()=>ho({current:void 0,version:oo.unknown()}),bo=ho,wo=(g(io),g(mo),g("IE"),g(uo),g(fo),g(go),"Windows"),vo="Android",yo="Linux",xo="macOS",Co="Solaris",So="FreeBSD",To="ChromeOS",Ro=e=>{const t=e.current,o=e.version,n=e=>()=>t===e;return{current:t,version:o,isWindows:n(wo),isiOS:n("iOS"),isAndroid:n(vo),isMacOS:n(xo),isLinux:n(yo),isSolaris:n(Co),isFreeBSD:n(So),isChromeOS:n(To)}},Do=()=>Ro({current:void 0,version:oo.unknown()}),Oo=Ro,ko=(g(wo),g("iOS"),g(vo),g(yo),g(xo),g(Co),g(So),g(To),e=>window.matchMedia(e).matches);let Eo=Zt((()=>((e,t,o)=>{const n=co.browsers(),r=co.oses(),s=t.bind((e=>((e,t)=>V(t.brands,(t=>{const o=t.brand.toLowerCase();return L(e,(e=>{var t;return o===(null===(t=e.brand)||void 0===t?void 0:t.toLowerCase())})).map((e=>({current:e.name,version:oo.nu(parseInt(t.version,10),0)})))})))(n,e))).orThunk((()=>((e,t)=>no(e,t).map((e=>{const o=oo.detect(e.versionRegexes,t);return{current:e.name,version:o}})))(n,e))).fold(po,bo),l=((e,t)=>no(e,t).map((e=>{const o=oo.detect(e.versionRegexes,t);return{current:e.name,version:o}})))(r,e).fold(Do,Oo),a=((e,t,o,n)=>{const r=e.isiOS()&&!0===/ipad/i.test(o),s=e.isiOS()&&!r,l=e.isiOS()||e.isAndroid(),a=l||n("(pointer:coarse)"),c=r||!s&&l&&n("(min-device-width:768px)"),i=s||l&&!c,m=t.isSafari()&&e.isiOS()&&!1===/safari/i.test(o),d=!i&&!c&&!m;return{isiPad:g(r),isiPhone:g(s),isTablet:g(c),isPhone:g(i),isTouch:g(a),isAndroid:e.isAndroid,isiOS:e.isiOS,isWebView:g(m),isDesktop:g(d)}})(l,s,e,o);return{browser:s,os:l,deviceType:a}})(navigator.userAgent,C.from(navigator.userAgentData),ko)));const No=()=>Eo(),Bo=(e,t)=>{const o=o=>{const n=t(o);if(n<=0||null===n){const t=_t(o,e);return parseFloat(t)||0}return n},n=(e,t)=>A(t,((t,o)=>{const n=_t(e,o),r=void 0===n?0:parseInt(n,10);return isNaN(r)?t:t+r}),0);return{set:(t,o)=>{if(!u(o)&&!o.match(/^[0-9]+$/))throw new Error(e+".set accepts only positive integer values. Value was "+o);const n=t.dom;kt(n)&&(n.style[e]=o+"px")},get:o,getOuter:o,aggregate:n,max:(e,t,o)=>{const r=n(e,o);return t>r?t-r:0}}},_o=(e,t,o)=>((e,t)=>(e=>{const t=parseFloat(e);return isNaN(t)?C.none():C.some(t)})(e).getOr(t))(_t(e,t),o),zo=Bo("width",(e=>e.dom.offsetWidth)),Ao=e=>zo.get(e),Lo=e=>zo.getOuter(e),Wo=e=>((e,t)=>{const o=e.dom,n=o.getBoundingClientRect().width||o.offsetWidth;return"border-box"===t?n:((e,t,o,n)=>t-_o(e,`padding-${o}`,0)-_o(e,`padding-${n}`,0)-_o(e,`border-${o}-width`,0)-_o(e,`border-${n}-width`,0))(e,n,"left","right")})(e,"content-box"),Mo=(e,t,o)=>{const n=e.cells,r=n.slice(0,t),s=n.slice(t),l=r.concat(o).concat(s);return Io(e,l)},jo=(e,t,o)=>Mo(e,t,[o]),Po=(e,t,o)=>{e.cells[t]=o},Io=(e,t)=>tt(e.element,t,e.section,e.isNew),Fo=(e,t)=>e.cells[t],Ho=(e,t)=>Fo(e,t).element,$o=e=>e.cells.length,Vo=e=>{const t=B(e,(e=>"colgroup"===e.section));return{rows:t.fail,cols:t.pass}},qo=(e,t,o)=>{const n=E(e.cells,o);return tt(t(e.element),n,e.section,!0)},Uo="data-snooker-locked-cols",Go=e=>be(e,Uo).bind((e=>C.from(e.match(/\d+/g)))).map((e=>I(e,x))),Ko=e=>{const t=A(Vo(e).rows,((e,t)=>(N(t.cells,((t,o)=>{t.isLocked&&(e[o]=!0)})),e)),{}),o=J(t,((e,t)=>parseInt(t,10)));return((e,t)=>{const o=S.call(e,0);return o.sort(void 0),o})(o)},Yo=(e,t)=>e+","+t,Jo=(e,t)=>{const o=j(e.all,(e=>e.cells));return _(o,t)},Qo=e=>{const t={},o=[],n=H(e).map((e=>e.element)).bind(Gt).bind(Go).getOr({});let r=0,s=0,l=0;const{pass:a,fail:c}=B(e,(e=>"colgroup"===e.section));N(c,(e=>{const a=[];N(e.cells,(e=>{let o=0;for(;void 0!==t[Yo(l,o)];)o++;const r=((e,t)=>X(e,t)&&void 0!==e[t]&&null!==e[t])(n,o.toString()),c=((e,t,o,n,r,s)=>({element:e,rowspan:t,colspan:o,row:n,column:r,isLocked:s}))(e.element,e.rowspan,e.colspan,l,o,r);for(let n=0;n{const t=(e=>{const t={};let o=0;return N(e.cells,(e=>{const n=e.colspan;k(n,(r=>{const s=o+r;t[s]=((e,t,o)=>({element:e,colspan:t,column:o}))(e.element,n,s)})),o+=n})),t})(e),o=((e,t)=>({element:e,columns:t}))(e.element,Q(t));return{colgroups:[o],columns:t}})).getOrThunk((()=>({colgroups:[],columns:{}}))),d=((e,t)=>({rows:e,columns:t}))(r,s);return{grid:d,access:t,all:o,columns:i,colgroups:m}},Xo=e=>{const t=Xt(e);return Qo(t)},Zo=Qo,en=(e,t,o)=>C.from(e.access[Yo(t,o)]),tn=(e,t,o)=>{const n=Jo(e,(e=>o(t,e.element)));return n.length>0?C.some(n[0]):C.none()},on=Jo,nn=e=>j(e.all,(e=>e.cells)),rn=e=>Q(e.columns),sn=e=>q(e.columns).length>0,ln=(e,t)=>C.from(e.columns[t]),an=(e,t=x)=>{const o=e.grid,n=k(o.columns,h),r=k(o.rows,h);return E(n,(o=>cn((()=>j(r,(t=>en(e,t,o).filter((e=>e.column===o)).toArray()))),(e=>1===e.colspan&&t(e.element)),(()=>en(e,0,o)))))},cn=(e,t,o)=>{const n=e();return L(n,t).orThunk((()=>C.from(n[0]).orThunk(o))).map((e=>e.element))},mn=e=>{const t=e.grid,o=k(t.rows,h),n=k(t.columns,h);return E(o,(t=>cn((()=>j(n,(o=>en(e,t,o).filter((e=>e.row===t)).fold(g([]),(e=>[e]))))),(e=>1===e.rowspan),(()=>en(e,t,0)))))},dn=(e,t)=>o=>"rtl"===un(o)?t:e,un=e=>"rtl"===_t(e,"direction")?"rtl":"ltr",fn=Bo("height",(e=>{const t=e.dom;return lt(e)?t.getBoundingClientRect().height:t.offsetHeight})),gn=e=>fn.get(e),hn=e=>fn.getOuter(e),pn=(e,t)=>({left:e,top:t,translate:(o,n)=>pn(e+o,t+n)}),bn=pn,wn=(e,t)=>void 0!==e?e:void 0!==t?t:0,vn=e=>{const t=e.dom.ownerDocument,o=t.body,n=t.defaultView,r=t.documentElement;if(o===e.dom)return bn(o.offsetLeft,o.offsetTop);const s=wn(null==n?void 0:n.pageYOffset,r.scrollTop),l=wn(null==n?void 0:n.pageXOffset,r.scrollLeft),a=wn(r.clientTop,o.clientTop),c=wn(r.clientLeft,o.clientLeft);return yn(e).translate(l-c,s-a)},yn=e=>{const t=e.dom,o=t.ownerDocument.body;return o===t?bn(o.offsetLeft,o.offsetTop):lt(e)?(e=>{const t=e.getBoundingClientRect();return bn(t.left,t.top)})(t):bn(0,0)},xn=(e,t)=>({row:e,y:t}),Cn=(e,t)=>({col:e,x:t}),Sn=e=>vn(e).left+Lo(e),Tn=e=>vn(e).left,Rn=(e,t)=>Cn(e,Tn(t)),Dn=(e,t)=>Cn(e,Sn(t)),On=e=>vn(e).top,kn=(e,t)=>xn(e,On(t)),En=(e,t)=>xn(e,On(t)+hn(t)),Nn=(e,t,o)=>{if(0===o.length)return[];const n=E(o.slice(1),((t,o)=>t.map((t=>e(o,t))))),r=o[o.length-1].map((e=>t(o.length-1,e)));return n.concat([r])},Bn={delta:h,positions:e=>Nn(kn,En,e),edge:On},_n=dn({delta:h,edge:Tn,positions:e=>Nn(Rn,Dn,e)},{delta:e=>-e,edge:Sn,positions:e=>Nn(Dn,Rn,e)}),zn={delta:(e,t)=>_n(t).delta(e,t),positions:(e,t)=>_n(t).positions(e,t),edge:e=>_n(e).edge(e)},An={unsupportedLength:["em","ex","cap","ch","ic","rem","lh","rlh","vw","vh","vi","vb","vmin","vmax","cm","mm","Q","in","pc","pt","px"],fixed:["px","pt"],relative:["%"],empty:[""]},Ln=(()=>{const e="[0-9]+",t="[eE][+-]?"+e,o=e=>`(?:${e})?`,n=["Infinity",e+"\\."+o(e)+o(t),"\\."+e+o(t),e+o(t)].join("|");return new RegExp(`^([+-]?(?:${n}))(.*)$`)})(),Wn=/(\d+(\.\d+)?)%/,Mn=/(\d+(\.\d+)?)px|em/,jn=ue("col"),Pn=ue("tr"),In=(e,t,o)=>{const n=Be(e).getOrThunk((()=>at(ke(e))));return t(e)/o(n)*100},Fn=(e,t)=>{Nt(e,"width",t+"px")},Hn=(e,t)=>{Nt(e,"width",t+"%")},$n=(e,t)=>{Nt(e,"height",t+"px")},Vn=e=>{const t=(e=>{return _o(t=e,"height",t.dom.offsetHeight)+"px";var t})(e);return t?((e,t,o,n)=>{const r=parseFloat(e);return Rt(e,"%")&&"table"!==ne(t)?((e,t,o,n)=>{const r=Gt(e).map((e=>{const n=o(e);return Math.floor(t/100*n)})).getOr(t);return n(e,r),r})(t,r,o,n):r})(t,e,gn,$n):gn(e)},qn=(e,t)=>At(e,t).orThunk((()=>be(e,t).map((e=>e+"px")))),Un=e=>qn(e,"width"),Gn=e=>In(e,Ao,Wo),Kn=e=>{return jn(e)?Ao(e):_o(t=e,"width",t.dom.offsetWidth);var t},Yn=e=>Pn(e)?gn(e):((e,t,o)=>o(e)/Mt(e,"rowspan"))(e,0,Vn),Jn=(e,t,o)=>{Nt(e,"width",t+o)},Qn=e=>In(e,Ao,Wo)+"%",Xn=g(Wn),Zn=ue("col"),er=e=>Un(e).getOrThunk((()=>Kn(e)+"px")),tr=e=>{return(t=e,qn(t,"height")).getOrThunk((()=>Yn(e)+"px"));var t},or=(e,t,o,n,r,s)=>e.filter(n).fold((()=>s(((e,t)=>{if(t<0||t>=e.length-1)return C.none();const o=e[t].fold((()=>{const o=(e=>{const t=S.call(e,0);return t.reverse(),t})(e.slice(0,t));return V(o,((e,t)=>e.map((e=>({value:e,delta:t+1})))))}),(e=>C.some({value:e,delta:0}))),n=e[t+1].fold((()=>{const o=e.slice(t+1);return V(o,((e,t)=>e.map((e=>({value:e,delta:t+1})))))}),(e=>C.some({value:e,delta:1})));return o.bind((e=>n.map((t=>{const o=t.delta+e.delta;return Math.abs(t.value-e.value)/o}))))})(o,t))),(e=>r(e))),nr=(e,t,o,n)=>{const r=an(e),s=sn(e)?(e=>E(rn(e),(e=>C.from(e.element))))(e):r,l=[C.some(zn.edge(t))].concat(E(zn.positions(r,t),(e=>e.map((e=>e.x))))),a=w(jt);return E(s,((e,t)=>or(e,t,l,a,(e=>{if((e=>{const t=No().browser,o=t.isChromium()||t.isFirefox();return!Zn(e)||o})(e))return o(e);{const e=null!=(s=r[t])?h(s):C.none();return or(e,t,l,a,(e=>n(C.some(Ao(e)))),n)}var s}),n)))},rr=e=>e.map((e=>e+"px")).getOr(""),sr=(e,t,o)=>nr(e,t,Kn,(e=>e.getOrThunk(o.minCellWidth))),lr=(e,t,o,n)=>{const r=mn(e),s=E(e.all,(e=>C.some(e.element))),l=[C.some(Bn.edge(t))].concat(E(Bn.positions(r,t),(e=>e.map((e=>e.y)))));return E(s,((e,t)=>or(e,t,l,x,o,n)))},ar=(e,t)=>()=>lt(e)?t(e):parseFloat(At(e,"width").getOr("0")),cr=e=>{const t=ar(e,(e=>parseFloat(Qn(e)))),o=ar(e,Ao);return{width:t,pixelWidth:o,getWidths:(t,o)=>((e,t,o)=>nr(e,t,Gn,(e=>e.fold((()=>o.minCellWidth()),(e=>e/o.pixelWidth()*100)))))(t,e,o),getCellDelta:e=>e/o()*100,singleColumnWidth:(e,t)=>[100-e],minCellWidth:()=>It()/o()*100,setElementWidth:Hn,adjustTableWidth:o=>{const n=t();Hn(e,n+o/100*n)},isRelative:!0,label:"percent"}},ir=e=>{const t=ar(e,Ao);return{width:t,pixelWidth:t,getWidths:(t,o)=>sr(t,e,o),getCellDelta:h,singleColumnWidth:(e,t)=>[Math.max(It(),e+t)-e],minCellWidth:It,setElementWidth:Fn,adjustTableWidth:o=>{const n=t()+o;Fn(e,n)},isRelative:!1,label:"pixel"}},mr=e=>Un(e).fold((()=>(e=>{const t=ar(e,Ao),o=g(0);return{width:t,pixelWidth:t,getWidths:(t,o)=>sr(t,e,o),getCellDelta:o,singleColumnWidth:g([0]),minCellWidth:o,setElementWidth:f,adjustTableWidth:f,isRelative:!0,label:"none"}})(e)),(t=>((e,t)=>null!==Xn().exec(t)?cr(e):ir(e))(e,t))),dr=ir,ur=cr,fr=(e,t,o)=>{const n=e[o].element,r=xe.fromTag("td");Ie(r,xe.fromTag("br")),(t?Ie:Pe)(n,r)},gr=((e,t)=>{const o=t=>e(t)?C.from(t.dom.nodeValue):C.none();return{get:t=>{if(!e(t))throw new Error("Can only get text value of a text node");return o(t).getOr("")},getOption:o,set:(t,o)=>{if(!e(t))throw new Error("Can only set raw text value of a text node");t.dom.nodeValue=o}}})(ie),hr=e=>gr.get(e),pr=e=>gr.getOption(e),br=(e,t)=>gr.set(e,t),wr=e=>"img"===ne(e)?1:pr(e).fold((()=>Le(e).length),(e=>e.length)),vr=["img","br"],yr=e=>pr(e).filter((e=>0!==e.trim().length||e.indexOf("\xa0")>-1)).isSome()||D(vr,ne(e))||(e=>ae(e)&&"false"===pe(e,"contenteditable"))(e),xr=e=>((e,t)=>{const o=e=>{for(let n=0;nSr(e,yr),Sr=(e,t)=>{const o=e=>{const n=Le(e);for(let e=n.length-1;e>=0;e--){const r=n[e];if(t(r))return C.some(r);const s=o(r);if(s.isSome())return s}return C.none()};return o(e)},Tr={scope:["row","col"]},Rr=e=>()=>{const t=xe.fromTag("td",e.dom);return Ie(t,xe.fromTag("br",e.dom)),t},Dr=e=>()=>xe.fromTag("col",e.dom),Or=e=>()=>xe.fromTag("colgroup",e.dom),kr=e=>()=>xe.fromTag("tr",e.dom),Er=(e,t,o)=>{const n=((e,t)=>{const o=Je(e,t),n=Le(Ye(e));return $e(o,n),o})(e,t);return G(o,((e,t)=>{null===e?we(n,t):ge(n,t,e)})),n},Nr=e=>e,Br=(e,t,o)=>{const n=(e,t)=>{((e,t)=>{const o=e.dom,n=t.dom;kt(o)&&kt(n)&&(n.style.cssText=o.style.cssText)})(e.element,t),Lt(t,"height"),1!==e.colspan&&Lt(t,"width")};return{col:o=>{const r=xe.fromTag(ne(o.element),t.dom);return n(o,r),e(o.element,r),r},colgroup:Or(t),row:kr(t),cell:r=>{const s=xe.fromTag(ne(r.element),t.dom),l=o.getOr(["strong","em","b","i","span","font","h1","h2","h3","h4","h5","h6","p","div"]),a=l.length>0?((e,t,o)=>xr(e).map((n=>{const r=o.join(","),s=it(n,r,(t=>Re(t,e)));return z(s,((e,t)=>{const o=Ke(t);return Ie(e,o),o}),t)})).getOr(t))(r.element,s,l):s;return Ie(a,xe.fromTag("br")),n(r,s),((e,t)=>{G(Tr,((o,n)=>be(e,n).filter((e=>D(o,e))).each((e=>ge(t,n,e)))))})(r.element,s),e(r.element,s),s},replace:Er,colGap:Dr(t),gap:Rr(t)}},_r=e=>({col:Dr(e),colgroup:Or(e),row:kr(e),cell:Rr(e),replace:Nr,colGap:Dr(e),gap:Rr(e)}),zr=e=>t=>t.options.get(e),Ar="100%",Lr=e=>{var t;const o=e.dom,n=null!==(t=o.getParent(e.selection.getStart(),o.isBlock))&&void 0!==t?t:e.getBody();return Wo(xe.fromDom(n))+"px"},Wr=e=>C.from(e.options.get("table_clone_elements")),Mr=zr("table_header_type"),jr=zr("table_column_resizing"),Pr=e=>"preservetable"===jr(e),Ir=e=>"resizetable"===jr(e),Fr=zr("table_sizing_mode"),Hr=e=>"relative"===Fr(e),$r=e=>"fixed"===Fr(e),Vr=e=>"responsive"===Fr(e),qr=zr("table_resize_bars"),Ur=zr("table_style_by_css"),Gr=zr("table_merge_content_on_paste"),Kr=e=>{const t=e.options,o=t.get("table_default_attributes");return t.isSet("table_default_attributes")?o:((e,t)=>Vr(e)||Ur(e)?t:$r(e)?{...t,width:Lr(e)}:{...t,width:Ar})(e,o)},Yr=zr("table_use_colgroups"),Jr=e=>wt(e,"[contenteditable]"),Qr=(e,t=!1)=>lt(e)?e.dom.isContentEditable:Jr(e).fold(g(t),(e=>"true"===Xr(e))),Xr=e=>e.dom.contentEditable,Zr=e=>xe.fromDom(e.getBody()),es=e=>t=>Re(t,Zr(e)),ts=e=>{we(e,"data-mce-style");const t=e=>we(e,"data-mce-style");N(qt(e),t),N(Ut(e),t),N(Kt(e),t)},os=e=>xe.fromDom(e.selection.getStart()),ns=e=>e.getBoundingClientRect().width,rs=e=>e.getBoundingClientRect().height,ss=e=>(t,o)=>{const n=t.dom.getStyle(o,e)||t.dom.getAttrib(o,e);return C.from(n).filter(Ot)},ls=ss("width"),as=ss("height"),cs=e=>gt(e,ue("table")).exists(Qr),is=(e,t)=>{const o=t.column,n=t.column+t.colspan-1,r=t.row,s=t.row+t.rowspan-1;return o<=e.finishCol&&n>=e.startCol&&r<=e.finishRow&&s>=e.startRow},ms=(e,t)=>t.column>=e.startCol&&t.column+t.colspan-1<=e.finishCol&&t.row>=e.startRow&&t.row+t.rowspan-1<=e.finishRow,ds=(e,t,o)=>{const n=tn(e,t,Re),r=tn(e,o,Re);return n.bind((e=>r.map((t=>{return o=e,n=t,{startRow:Math.min(o.row,n.row),startCol:Math.min(o.column,n.column),finishRow:Math.max(o.row+o.rowspan-1,n.row+n.rowspan-1),finishCol:Math.max(o.column+o.colspan-1,n.column+n.colspan-1)};var o,n}))))},us=(e,t,o)=>ds(e,t,o).map((t=>{const o=on(e,b(is,t));return E(o,(e=>e.element))})),fs=(e,t)=>tn(e,t,((e,t)=>De(t,e))).map((e=>e.element)),gs=(e,t,o)=>{const n=ps(e);return us(n,t,o)},hs=(e,t,o,n,r)=>{const s=ps(e),l=Re(e,o)?C.some(t):fs(s,t),a=Re(e,r)?C.some(n):fs(s,n);return l.bind((e=>a.bind((t=>us(s,e,t)))))},ps=Xo;var bs=["body","p","div","article","aside","figcaption","figure","footer","header","nav","section","ol","ul","li","table","thead","tbody","tfoot","caption","tr","td","th","h1","h2","h3","h4","h5","h6","blockquote","pre","address"],ws=()=>({up:g({selector:ht,closest:wt,predicate:ft,all:_e}),down:g({selector:dt,predicate:ct}),styles:g({get:_t,getRaw:At,set:Nt,remove:Lt}),attrs:g({get:pe,set:ge,remove:we,copyTo:(e,t)=>{const o=ve(e);he(t,o)}}),insert:g({before:Me,after:je,afterAll:He,append:Ie,appendAll:$e,prepend:Pe,wrap:Fe}),remove:g({unwrap:Ue,remove:qe}),create:g({nu:xe.fromTag,clone:e=>xe.fromDom(e.dom.cloneNode(!1)),text:xe.fromText}),query:g({comparePosition:(e,t)=>e.dom.compareDocumentPosition(t.dom),prevSibling:ze,nextSibling:Ae}),property:g({children:Le,name:ne,parent:Ne,document:e=>Ee(e).dom,isText:ie,isComment:le,isElement:ce,isSpecial:e=>{const t=ne(e);return D(["script","noscript","iframe","noframes","noembed","title","style","textarea","xmp"],t)},getLanguage:e=>ce(e)?be(e,"lang"):C.none(),getText:hr,setText:br,isBoundary:e=>!!ce(e)&&("body"===ne(e)||D(bs,ne(e))),isEmptyTag:e=>!!ce(e)&&D(["br","img","hr","input"],ne(e)),isNonEditable:e=>ce(e)&&"false"===pe(e,"contenteditable")}),eq:Re,is:Oe});const vs=(e,t,o,n)=>{const r=t(e,o);return z(n,((o,n)=>{const r=t(e,n);return ys(e,o,r)}),r)},ys=(e,t,o)=>t.bind((t=>o.filter(b(e.eq,t)))),xs=ws(),Cs=(e,t)=>((e,t,o)=>o.length>0?((e,t,o,n)=>n(e,t,o[0],o.slice(1)))(e,t,o,vs):C.none())(xs,((t,o)=>e(o)),t),Ss=e=>ht(e,"table"),Ts=(e,t,o)=>{const n=e=>t=>void 0!==o&&o(t)||Re(t,e);return Re(e,t)?C.some({boxes:C.some([e]),start:e,finish:t}):Ss(e).bind((r=>Ss(t).bind((s=>{if(Re(r,s))return C.some({boxes:gs(r,e,t),start:e,finish:t});if(De(r,s)){const o=it(t,"td,th",n(r)),l=o.length>0?o[o.length-1]:t;return C.some({boxes:hs(r,e,r,t,s),start:e,finish:l})}if(De(s,r)){const o=it(e,"td,th",n(s)),l=o.length>0?o[o.length-1]:e;return C.some({boxes:hs(s,e,r,t,s),start:e,finish:l})}return((e,t,o)=>((e,t,o,n=y)=>{const r=[t].concat(e.up().all(t)),s=[o].concat(e.up().all(o)),l=e=>W(e,n).fold((()=>e),(t=>e.slice(0,t+1))),a=l(r),c=l(s),i=L(a,(t=>O(c,((e,t)=>b(e.eq,t))(e,t))));return{firstpath:a,secondpath:c,shared:i}})(xs,e,t,void 0))(e,t).shared.bind((l=>wt(l,"table",o).bind((o=>{const l=it(t,"td,th",n(o)),a=l.length>0?l[l.length-1]:t,c=it(e,"td,th",n(o)),i=c.length>0?c[c.length-1]:e;return C.some({boxes:hs(o,e,r,t,s),start:i,finish:a})}))))}))))},Rs=(e,t)=>{const o=dt(e,t);return o.length>0?C.some(o):C.none()},Ds=(e,t,o)=>bt(e,t).bind((t=>bt(e,o).bind((e=>Cs(Ss,[t,e]).map((o=>({first:t,last:e,table:o}))))))),Os=(e,t,o,n,r)=>((e,t)=>L(e,(e=>Ce(e,t))))(e,r).bind((e=>((e,t,o)=>Gt(e).bind((n=>((e,t,o,n)=>tn(e,t,Re).bind((t=>{const r=o>0?t.row+t.rowspan-1:t.row,s=n>0?t.column+t.colspan-1:t.column;return en(e,r+o,s+n).map((e=>e.element))})))(ps(n),e,t,o))))(e,t,o).bind((e=>((e,t)=>ht(e,"table").bind((o=>bt(o,t).bind((t=>Ts(t,e).bind((e=>e.boxes.map((t=>({boxes:t,start:e.start,finish:e.finish}))))))))))(e,n))))),ks=(e,t)=>Rs(e,t),Es=(e,t,o)=>Ds(e,t,o).bind((t=>{const o=t=>Re(e,t),n="thead,tfoot,tbody,table",r=ht(t.first,n,o),s=ht(t.last,n,o);return r.bind((e=>s.bind((o=>Re(e,o)?((e,t,o)=>((e,t,o)=>ds(e,t,o).bind((t=>((e,t)=>{let o=!0;const n=b(ms,t);for(let r=t.startRow;r<=t.finishRow;r++)for(let s=t.startCol;s<=t.finishCol;s++)o=o&&en(e,r,s).exists(n);return o?C.some(t):C.none()})(e,t))))(ps(e),t,o))(t.table,t.first,t.last):C.none()))))})),Ns=h,Bs=e=>{const t=(e,t)=>be(e,t).exists((e=>parseInt(e,10)>1));return e.length>0&&P(e,(e=>t(e,"rowspan")||t(e,"colspan")))?C.some(e):C.none()},_s=(e,t,o)=>t.length<=1?C.none():Es(e,o.firstSelectedSelector,o.lastSelectedSelector).map((e=>({bounds:e,cells:t}))),zs="data-mce-selected",As="data-mce-first-selected",Ls="data-mce-last-selected",Ws="["+zs+"]",Ms={selected:zs,selectedSelector:"td["+zs+"],th["+zs+"]",firstSelected:As,firstSelectedSelector:"td["+As+"],th["+As+"]",lastSelected:Ls,lastSelectedSelector:"td["+Ls+"],th["+Ls+"]"},js=(e,t,o)=>({element:o,mergable:_s(t,e,Ms),unmergable:Bs(e),selection:Ns(e)}),Ps=e=>(t,o)=>{const n=ne(t),r="col"===n||"colgroup"===n?Gt(s=t).bind((e=>ks(e,Ms.firstSelectedSelector))).fold(g(s),(e=>e[0])):t;var s;return wt(r,e,o)},Is=Ps("th,td,caption"),Fs=Ps("th,td"),Hs=e=>{return t=e.model.table.getSelectedCells(),E(t,xe.fromDom);var t},$s=(e,t)=>{e.on("BeforeGetContent",(t=>{const o=o=>{t.preventDefault(),(e=>Gt(e[0]).map((e=>{const t=((e,t)=>{const o=e=>Ce(e.element,t),n=Ye(e),r=Xt(n),s=mr(e),l=Zo(r),a=((e,t)=>{const o=e.grid.columns;let n=e.grid.rows,r=o,s=0,l=0;const a=[],c=[];return G(e.access,(e=>{if(a.push(e),t(e)){c.push(e);const t=e.row,o=t+e.rowspan-1,a=e.column,i=a+e.colspan-1;ts&&(s=o),al&&(l=i)}})),((e,t,o,n,r,s)=>({minRow:e,minCol:t,maxRow:o,maxCol:n,allCells:r,selectedCells:s}))(n,r,s,l,a,c)})(l,o),c="th:not("+t+"),td:not("+t+")",i=$t(n,"th,td",(e=>Ce(e,c)));N(i,qe),((e,t,o,n)=>{const r=_(e,(e=>"colgroup"!==e.section)),s=t.grid.columns,l=t.grid.rows;for(let e=0;eo.maxRow||ao.maxCol||(en(t,e,a).filter(n).isNone()?fr(r,l,e):l=!0)}})(r,l,a,o);const m=((e,t,o,n)=>{if(0===n.minCol&&t.grid.columns===n.maxCol+1)return 0;const r=sr(t,e,o),s=A(r,((e,t)=>e+t),0),l=A(r.slice(n.minCol,n.maxCol+1),((e,t)=>e+t),0),a=l/s*o.pixelWidth()-o.pixelWidth();return o.getCellDelta(a)})(e,Xo(e),s,a);return((e,t,o,n)=>{G(o.columns,(e=>{(e.columnt.maxCol)&&qe(e.element)}));const r=_(Ht(e,"tr"),(e=>0===e.dom.childElementCount));N(r,qe),t.minCol!==t.maxCol&&t.minRow!==t.maxRow||N(Ht(e,"th,td"),(e=>{we(e,"rowspan"),we(e,"colspan")})),we(e,Uo),we(e,"data-snooker-col-series"),mr(e).adjustTableWidth(n)})(n,a,l,m),n})(e,Ws);return ts(t),[t]})))(o).each((o=>{t.content="text"===t.format?(e=>E(e,(e=>e.dom.innerText)).join(""))(o):((e,t)=>E(t,(t=>e.selection.serializer.serialize(t.dom,{}))).join(""))(e,o)}))};if(!0===t.selection){const t=(e=>_(Hs(e),(e=>Ce(e,Ms.selectedSelector))))(e);t.length>=1&&o(t)}})),e.on("BeforeSetContent",(o=>{if(!0===o.selection&&!0===o.paste){const n=Hs(e);H(n).each((n=>{Gt(n).each((r=>{const s=_(((e,t)=>{const o=document.createElement("div");return o.innerHTML=e,Le(xe.fromDom(o))})(o.content),(e=>"meta"!==ne(e))),l=ue("table");if(Gr(e)&&1===s.length&&l(s[0])){o.preventDefault();const l=xe.fromDom(e.getDoc()),a=_r(l),c=((e,t,o)=>({element:e,clipboard:t,generators:o}))(n,s[0],a);t.pasteCells(r,c).each((()=>{e.focus()}))}}))}))}}))},Vs=(e,t)=>({element:e,offset:t}),qs=(e,t,o)=>e.property().isText(t)&&0===e.property().getText(t).trim().length||e.property().isComment(t)?o(t).bind((t=>qs(e,t,o).orThunk((()=>C.some(t))))):C.none(),Us=(e,t)=>e.property().isText(t)?e.property().getText(t).length:e.property().children(t).length,Gs=(e,t)=>{const o=qs(e,t,e.query().prevSibling).getOr(t);if(e.property().isText(o))return Vs(o,Us(e,o));const n=e.property().children(o);return n.length>0?Gs(e,n[n.length-1]):Vs(o,Us(e,o))},Ks=Gs,Ys=ws(),Js=(e,t)=>{if(!jt(e)){const o=(e=>Un(e).bind((e=>{return t=e,o=["fixed","relative","empty"],C.from(Ln.exec(t)).bind((e=>{const t=Number(e[1]),n=e[2];return((e,t)=>O(t,(t=>O(An[t],(t=>e===t)))))(n,o)?C.some({value:t,unit:n}):C.none()}));var t,o})))(e);o.each((o=>{const n=o.value/2;Jn(e,n,o.unit),Jn(t,n,o.unit)}))}},Qs=e=>E(e,g(0)),Xs=(e,t,o,n,r)=>r(e.slice(0,t)).concat(n).concat(r(e.slice(o))),Zs=e=>(t,o,n,r)=>{if(e(n)){const e=Math.max(r,t[o]-Math.abs(n)),s=Math.abs(e-t[o]);return n>=0?s:-s}return n},el=Zs((e=>e<0)),tl=Zs(x),ol=()=>{const e=(e,t,o,n)=>{const r=(100+o)/100,s=Math.max(n,(e[t]+o)/r);return E(e,((e,o)=>(o===t?s:e/r)-e))},t=(t,o,n,r,s,l)=>l?e(t,o,r,s):((e,t,o,n,r)=>{const s=el(e,t,n,r);return Xs(e,t,o+1,[s,0],Qs)})(t,o,n,r,s);return{resizeTable:(e,t)=>e(t),clampTableDelta:el,calcLeftEdgeDeltas:t,calcMiddleDeltas:(e,o,n,r,s,l,a)=>t(e,n,r,s,l,a),calcRightEdgeDeltas:(t,o,n,r,s,l)=>{if(l)return e(t,n,r,s);{const e=el(t,n,r,s);return Qs(t.slice(0,n)).concat([e])}},calcRedestributedWidths:(e,t,o,n)=>{if(n){const n=(t+o)/t,r=E(e,(e=>e/n));return{delta:100*n-100,newSizes:r}}return{delta:o,newSizes:e}}}},nl=()=>{const e=(e,t,o,n,r)=>{const s=tl(e,n>=0?o:t,n,r);return Xs(e,t,o+1,[s,-s],Qs)};return{resizeTable:(e,t,o)=>{o&&e(t)},clampTableDelta:(e,t,o,n,r)=>{if(r){if(o>=0)return o;{const t=A(e,((e,t)=>e+t-n),0);return Math.max(-t,o)}}return el(e,t,o,n)},calcLeftEdgeDeltas:e,calcMiddleDeltas:(t,o,n,r,s,l)=>e(t,n,r,s,l),calcRightEdgeDeltas:(e,t,o,n,r,s)=>{if(s)return Qs(e);{const t=n/e.length;return E(e,g(t))}},calcRedestributedWidths:(e,t,o,n)=>({delta:0,newSizes:e})}},rl=e=>Xo(e).grid,sl=ue("th"),ll=e=>P(e,(e=>sl(e.element))),al=(e,t)=>e&&t?"sectionCells":e?"section":"cells",cl=e=>{const t="thead"===e.section,o=vt(il(e.cells),"th");return"tfoot"===e.section?{type:"footer"}:t||o?{type:"header",subType:al(t,o)}:{type:"body"}},il=e=>{const t=_(e,(e=>sl(e.element)));return 0===t.length?C.some("td"):t.length===e.length?C.some("th"):C.none()},ml=(e,t,o)=>et(o(e.element,t),!0,e.isLocked),dl=(e,t)=>e.section!==t?tt(e.element,e.cells,t,e.isNew):e,ul=()=>({transformRow:dl,transformCell:(e,t,o)=>{const n=o(e.element,t),r="td"!==ne(n)?((e,t)=>{const o=Je(e,"td");je(e,o);const n=Le(e);return $e(o,n),qe(e),o})(n):n;return et(r,e.isNew,e.isLocked)}}),fl=()=>({transformRow:dl,transformCell:ml}),gl=()=>({transformRow:(e,t)=>dl(e,"thead"===t?"tbody":t),transformCell:ml}),hl=ul,pl=fl,bl=gl,wl=()=>({transformRow:h,transformCell:ml}),vl=(e,t,o,n)=>{o===n?we(e,t):ge(e,t,o)},yl=(e,t,o)=>{$(mt(e,t)).fold((()=>Pe(e,o)),(e=>je(e,o)))},xl=(e,t)=>{const o=[],n=[],r=e=>E(e,(e=>{e.isNew&&o.push(e.element);const t=e.element;return Ve(t),N(e.cells,(e=>{e.isNew&&n.push(e.element),vl(e.element,"colspan",e.colspan,1),vl(e.element,"rowspan",e.rowspan,1),Ie(t,e.element)})),t})),s=e=>j(e,(e=>E(e.cells,(e=>(vl(e.element,"span",e.colspan,1),e.element))))),l=(t,o)=>{const n=((e,t)=>{const o=pt(e,t).getOrThunk((()=>{const o=xe.fromTag(t,ke(e).dom);return"thead"===t?yl(e,"caption,colgroup",o):"colgroup"===t?yl(e,"caption",o):Ie(e,o),o}));return Ve(o),o})(e,o),l=("colgroup"===o?s:r)(t);$e(n,l)},a=(t,o)=>{t.length>0?l(t,o):(t=>{pt(e,t).each(qe)})(o)},c=[],i=[],m=[],d=[];return N(t,(e=>{switch(e.section){case"thead":c.push(e);break;case"tbody":i.push(e);break;case"tfoot":m.push(e);break;case"colgroup":d.push(e)}})),a(d,"colgroup"),a(c,"thead"),a(i,"tbody"),a(m,"tfoot"),{newRows:o,newCells:n}},Cl=(e,t)=>{if(0===e.length)return 0;const o=e[0];return W(e,(e=>!t(o.element,e.element))).getOr(e.length)},Sl=(e,t)=>{const o=E(e,(e=>E(e.cells,y)));return E(e,((n,r)=>{const s=j(n.cells,((n,s)=>{if(!1===o[r][s]){const m=((e,t,o,n)=>{const r=((e,t)=>e[t])(e,t),s="colgroup"===r.section,l=Cl(r.cells.slice(o),n),a=s?1:Cl(((e,t)=>E(e,(e=>Fo(e,t))))(e.slice(t),o),n);return{colspan:l,rowspan:a}})(e,r,s,t);return((e,t,n,r)=>{for(let s=e;s({element:e,cells:t,section:o,isNew:n}))(n.element,s,n.section,n.isNew)}))},Tl=(e,t,o)=>{const n=[];N(e.colgroups,(r=>{const s=[];for(let n=0;net(e.element,o,!1))).getOrThunk((()=>et(t.colGap(),!0,!1)));s.push(r)}n.push(tt(r.element,s,"colgroup",o))}));for(let r=0;ret(e.element,o,e.isLocked))).getOrThunk((()=>et(t.gap(),!0,!1)));s.push(l)}const l=e.all[r],a=tt(l.element,s,l.section,o);n.push(a)}return n},Rl=e=>Sl(e,Re),Dl=(e,t)=>V(e.all,(e=>L(e.cells,(e=>Re(t,e.element))))),Ol=(e,t,o)=>{const n=E(t.selection,(t=>Vt(t).bind((t=>Dl(e,t))).filter(o))),r=yt(n);return xt(r.length>0,r)},kl=(e,t,o,n,r)=>(s,l,a,c)=>{const i=Xo(s),m=C.from(null==c?void 0:c.section).getOrThunk(wl);return t(i,l).map((t=>{const o=((e,t)=>Tl(e,t,!1))(i,a),n=e(o,t,Re,r(a),m),s=Ko(n.grid);return{info:t,grid:Rl(n.grid),cursor:n.cursor,lockedColumns:s}})).bind((e=>{const t=xl(s,e.grid),r=C.from(null==c?void 0:c.sizing).getOrThunk((()=>mr(s))),l=C.from(null==c?void 0:c.resize).getOrThunk(nl);return o(s,e.grid,e.info,{sizing:r,resize:l,section:m}),n(s),we(s,Uo),e.lockedColumns.length>0&&ge(s,Uo,e.lockedColumns.join(",")),C.some({cursor:e.cursor,newRows:t.newRows,newCells:t.newCells})}))},El=(e,t)=>Ol(e,t,x).map((e=>({cells:e,generators:t.generators,clipboard:t.clipboard}))),Nl=(e,t)=>Ol(e,t,x),Bl=(e,t)=>Ol(e,t,(e=>!e.isLocked)),_l=(e,t)=>P(t,(t=>((e,t)=>Dl(e,t).exists((e=>!e.isLocked)))(e,t))),zl=(e,t,o,n)=>{const r=Vo(e).rows;let s=!0;for(let e=0;e{const t=t=>t(e),o=g(e),n=()=>r,r={tag:!0,inner:e,fold:(t,o)=>o(e),isValue:x,isError:y,map:t=>Wl.value(t(e)),mapError:n,bind:t,exists:t,forall:t,getOr:o,or:n,getOrThunk:o,orThunk:n,getOrDie:o,each:t=>{t(e)},toOptional:()=>C.some(e)};return r},Ll=e=>{const t=()=>o,o={tag:!1,inner:e,fold:(t,o)=>t(e),isValue:y,isError:x,map:t,mapError:t=>Wl.error(t(e)),bind:t,exists:y,forall:x,getOr:h,or:h,getOrThunk:v,orThunk:v,getOrDie:(n=String(e),()=>{throw new Error(n)}),each:f,toOptional:C.none};var n;return o},Wl={value:Al,error:Ll,fromOption:(e,t)=>e.fold((()=>Ll(t)),Al)},Ml=(e,t)=>({rowDelta:0,colDelta:$o(e[0])-$o(t[0])}),jl=(e,t)=>({rowDelta:e.length-t.length,colDelta:0}),Pl=(e,t,o,n)=>{const r="colgroup"===t.section?o.col:o.cell;return k(e,(e=>et(r(),!0,n(e))))},Il=(e,t,o,n)=>{const r=e[e.length-1];return e.concat(k(t,(()=>{const e="colgroup"===r.section?o.colgroup:o.row,t=qo(r,e,h),s=Pl(t.cells.length,t,o,(e=>X(n,e.toString())));return Io(t,s)})))},Fl=(e,t,o,n)=>E(e,(e=>{const r=Pl(t,e,o,y);return Mo(e,n,r)})),Hl=(e,t,o)=>{const n=t.colDelta<0?Fl:h,r=t.rowDelta<0?Il:h,s=Ko(e),l=$o(e[0]),a=O(s,(e=>e===l-1)),c=n(e,Math.abs(t.colDelta),o,a?l-1:l),i=Ko(c);return r(c,Math.abs(t.rowDelta),o,I(i,x))},$l=(e,t,o,n)=>{const r=b(n,Fo(e[t],o).element),s=e[t];return e.length>1&&$o(s)>1&&(o>0&&r(Ho(s,o-1))||o0&&r(Ho(e[t-1],o))||t_(o,(o=>o>=e.column&&o<=$o(t[0])+e.column)),ql=(e,t,o,n,r)=>{((e,t,o,n)=>{t>0&&t{const r=e.cells[t-1];let s=0;const l=n();for(;e.cells.length>t+s&&o(r.element,e.cells[t+s].element);)Po(e,t+s,et(l,!0,e.cells[t+s].isLocked)),s++}))})(t,e,r,n.cell);const s=jl(o,t),l=Hl(o,s,n),a=jl(t,l),c=Hl(t,a,n);return E(c,((t,o)=>Mo(t,e,l[o].cells)))},Ul=(e,t,o,n,r)=>{((e,t,o,n)=>{const r=Vo(e).rows;if(t>0&&tA(e,((e,o)=>O(e,(e=>t(e.element,o.element)))?e:e.concat([o])),[]))(r[t-1].cells,o);N(e,(e=>{let s=C.none();for(let l=t;l{Po(a,t,et(e,!0,c.isLocked))})))}}))}})(t,e,r,n.cell);const s=Ko(t),l=Ml(t,o),a={...l,colDelta:l.colDelta-s.length},c=Hl(t,a,n),{cols:i,rows:m}=Vo(c),d=Ko(c),u=Ml(o,t),f={...u,colDelta:u.colDelta+d.length},g=(p=n,b=d,E(o,(e=>A(b,((t,o)=>{const n=Pl(1,e,p,x)[0];return jo(t,o,n)}),e)))),h=Hl(g,f,n);var p,b;return[...i,...m.slice(0,e),...h,...m.slice(e,m.length)]},Gl=(e,t,o,n,r)=>{const{rows:s,cols:l}=Vo(e),a=s.slice(0,t),c=s.slice(t);return[...l,...a,((e,t,o,n)=>qo(e,(e=>n(e,o)),t))(s[o],((e,o)=>t>0&&tE(e,(e=>{const s=t>0&&t<$o(e)&&n(Ho(e,t-1),Ho(e,t)),l=((e,t,o,n,r,s,l)=>{if("colgroup"!==o&&n)return Fo(e,t);{const t=Fo(e,r);return et(l(t.element,s),!0,!1)}})(e,t,e.section,s,o,n,r);return jo(e,t,l)})),Yl=(e,t,o,n)=>((e,t,o,n)=>void 0!==Ho(e[t],o)&&t>0&&n(Ho(e[t-1],o),Ho(e[t],o)))(e,t,o,n)||((e,t,o)=>t>0&&o(Ho(e,t-1),Ho(e,t)))(e[t],o,n),Jl=(e,t,o,n)=>{const r=e=>(e=>"row"===e?(e=>Mt(e,"rowspan")>1)(t):jt(t))(e)?`${e}group`:e;return e?sl(t)?r(o):null:n&&sl(t)?r("row"===o?"col":"row"):null},Ql=(e,t,o)=>et(o(e.element,t),!0,e.isLocked),Xl=(e,t,o,n,r,s,l)=>E(e,((e,a)=>((e,c)=>{const i=e.cells,m=E(i,((e,c)=>{if((e=>O(t,(t=>o(e.element,t.element))))(e)){const t=l(e,a,c)?r(e,o,n):e;return s(t,a,c).each((e=>{var o,n;o=t.element,n={scope:C.from(e)},G(n,((e,t)=>{e.fold((()=>{we(o,t)}),(e=>{fe(o.dom,t,e)}))}))})),t}return e}));return tt(e.element,m,e.section,e.isNew)})(e))),Zl=(e,t,o)=>j(e,((n,r)=>Yl(e,r,t,o)?[]:[Fo(n,t)])),ea=(e,t,o,n,r)=>{const s=Vo(e).rows,l=j(t,(e=>Zl(s,e,n))),a=E(s,(e=>ll(e.cells))),c=((e,t)=>P(t,h)&&ll(e)?x:(e,o,n)=>!("th"===ne(e.element)&&t[o]))(l,a),i=((e,t)=>(o,n)=>C.some(Jl(e,o.element,"row",t[n])))(o,a);return Xl(e,l,n,r,Ql,i,c)},ta=(e,t,o,n)=>{const r=Vo(e).rows,s=E(t,(e=>Fo(r[e.row],e.column)));return Xl(e,s,o,n,Ql,C.none,x)},oa=e=>{if(!l(e))throw new Error("cases must be an array");if(0===e.length)throw new Error("there must be at least one case");const t=[],o={};return N(e,((n,r)=>{const s=q(n);if(1!==s.length)throw new Error("one and only one name per case");const a=s[0],c=n[a];if(void 0!==o[a])throw new Error("duplicate key detected:"+a);if("cata"===a)throw new Error("cannot have a case named cata (sorry)");if(!l(c))throw new Error("case arguments must be an array");t.push(a),o[a]=(...o)=>{const n=o.length;if(n!==c.length)throw new Error("Wrong number of arguments to case "+a+". Expected "+c.length+" ("+c+"), got "+n);return{fold:(...t)=>{if(t.length!==e.length)throw new Error("Wrong number of arguments to fold. Expected "+e.length+", got "+t.length);return t[r].apply(null,o)},match:e=>{const n=q(e);if(t.length!==n.length)throw new Error("Wrong number of arguments to match. Expected: "+t.join(",")+"\nActual: "+n.join(","));if(!P(t,(e=>D(n,e))))throw new Error("Not all branches were specified when using match. Specified: "+n.join(", ")+"\nRequired: "+t.join(", "));return e[a].apply(null,o)},log:e=>{console.log(e,{constructors:t,constructor:a,params:o})}}}})),o},na={...oa([{none:[]},{only:["index"]},{left:["index","next"]},{middle:["prev","index","next"]},{right:["prev","index"]}])},ra=(e,t,o)=>{const n=((e,t)=>sn(e)?((e,t)=>{const o=rn(e);return E(o,((e,o)=>({element:e.element,width:t[o],colspan:e.colspan})))})(e,t):((e,t)=>{const o=nn(e);return E(o,(e=>{const o=((e,t,o)=>{let n=0;for(let r=e;r{o.setElementWidth(e.element,e.width)}))},sa=(e,t,o,n,r)=>{const s=Xo(e),l=r.getCellDelta(t),a=r.getWidths(s,r),c=o===s.grid.columns-1,i=n.clampTableDelta(a,o,l,r.minCellWidth(),c),m=((e,t,o,n,r)=>{const s=e.slice(0),l=((e,t)=>0===e.length?na.none():1===e.length?na.only(0):0===t?na.left(0,1):t===e.length-1?na.right(t-1,t):t>0&&tn.singleColumnWidth(s[e],o)),((e,t)=>r.calcLeftEdgeDeltas(s,e,t,o,n.minCellWidth(),n.isRelative)),((e,t,l)=>r.calcMiddleDeltas(s,e,t,l,o,n.minCellWidth(),n.isRelative)),((e,t)=>r.calcRightEdgeDeltas(s,e,t,o,n.minCellWidth(),n.isRelative)))})(a,o,i,r,n),d=E(m,((e,t)=>e+a[t]));ra(s,d,r),n.resizeTable(r.adjustTableWidth,i,c)},la=(e,t,o)=>{const n=Xo(e),r=((e,t)=>lr(e,t,Yn,(e=>e.getOrThunk(Ft))))(n,e),s=E(r,((e,n)=>o===n?Math.max(t+e,Ft()):e)),l=((e,t)=>E(e.all,((e,o)=>({element:e.element,height:t[o]}))))(n,s);N(l,(e=>{$n(e.element,e.height)})),N(nn(n),(e=>{(e=>{Lt(e,"height")})(e.element)}));const a=z(s,((e,t)=>e+t),0);$n(e,a)},aa=e=>A(e,((e,t)=>O(e,(e=>e.column===t.column))?e:e.concat([t])),[]).sort(((e,t)=>e.column-t.column)),ca=ue("col"),ia=ue("colgroup"),ma=e=>"tr"===ne(e)||ia(e),da=e=>({element:e,colspan:Wt(e,"colspan",1),rowspan:Wt(e,"rowspan",1)}),ua=e=>be(e,"scope").map((e=>e.substr(0,3))),fa=(e,t=da)=>{const o=o=>{if(ma(o))return ia((r={element:o}).element)?e.colgroup(r):e.row(r);{const r=o,s=(t=>ca(t.element)?e.col(t):e.cell(t))(t(r));return n=C.some({item:r,replacement:s}),s}var r};let n=C.none();return{getOrInit:(e,t)=>n.fold((()=>o(e)),(n=>t(e,n.item)?n.replacement:o(e)))}},ga=e=>t=>{const o=[],n=n=>{const r="td"===e?{scope:null}:{},s=t.replace(n,e,r);return o.push({item:n,sub:s}),s};return{replaceOrInit:(e,t)=>{if(ma(e)||ca(e))return e;{const r=e;return((e,t)=>L(o,(o=>t(o.item,e))))(r,t).fold((()=>n(r)),(o=>t(e,o.item)?o.sub:n(r)))}}}},ha=e=>({unmerge:t=>{const o=ua(t);return o.each((e=>ge(t,"scope",e))),()=>{const n=e.cell({element:t,colspan:1,rowspan:1});return Lt(n,"width"),Lt(t,"width"),o.each((e=>ge(n,"scope",e))),n}},merge:e=>(Lt(e[0],"width"),(()=>{const t=yt(E(e,ua));if(0===t.length)return C.none();{const e=t[0],o=["row","col"];return O(t,(t=>t!==e&&D(o,t)))?C.none():C.from(e)}})().fold((()=>we(e[0],"scope")),(t=>ge(e[0],"scope",t+"group"))),g(e[0]))}),pa=["body","p","div","article","aside","figcaption","figure","footer","header","nav","section","ol","ul","table","thead","tfoot","tbody","caption","tr","td","th","h1","h2","h3","h4","h5","h6","blockquote","pre","address"],ba=ws(),wa=e=>((e,t)=>{const o=e.property().name(t);return D(pa,o)})(ba,e),va=e=>((e,t)=>{const o=e.property().name(t);return D(["ol","ul"],o)})(ba,e),ya=e=>{const t=ue("br"),o=e=>Cr(e).bind((o=>{const n=Ae(o).map((e=>!!wa(e)||!!((e,t)=>D(["br","img","hr","input"],e.property().name(t)))(ba,e)&&"img"!==ne(e))).getOr(!1);return Ne(o).map((r=>{return!0===n||("li"===ne(s=r)||ft(s,va).isSome())||t(o)||wa(r)&&!Re(e,r)?[]:[xe.fromTag("br")];var s}))})).getOr([]),n=(()=>{const n=j(e,(e=>{const n=Le(e);return(e=>P(e,(e=>t(e)||ie(e)&&0===hr(e).trim().length)))(n)?[]:n.concat(o(e))}));return 0===n.length?[xe.fromTag("br")]:n})();Ve(e[0]),$e(e[0],n)},xa=e=>Qr(e,!0),Ca=e=>{0===qt(e).length&&qe(e)},Sa=(e,t)=>({grid:e,cursor:t}),Ta=(e,t,o)=>{const n=((e,t,o)=>{var n,r;const s=Vo(e).rows;return C.from(null===(r=null===(n=s[t])||void 0===n?void 0:n.cells[o])||void 0===r?void 0:r.element).filter(xa).orThunk((()=>(e=>V(e,(e=>V(e.cells,(e=>{const t=e.element;return xt(xa(t),t)})))))(s)))})(e,t,o);return Sa(e,n)},Ra=e=>A(e,((e,t)=>O(e,(e=>e.row===t.row))?e:e.concat([t])),[]).sort(((e,t)=>e.row-t.row)),Da=(e,t)=>(o,n,r,s,l)=>{const a=Ra(n),c=E(a,(e=>e.row)),i=((e,t,o,n,r,s,l)=>{const{cols:a,rows:c}=Vo(e),i=c[t[0]],m=j(t,(e=>((e,t,o)=>{const n=e[t];return j(n.cells,((n,r)=>Yl(e,t,r,o)?[]:[n]))})(c,e,r))),d=E(i.cells,((e,t)=>ll(Zl(c,t,r)))),u=[...c];N(t,(e=>{u[e]=l.transformRow(c[e],o)}));const f=[...a,...u],g=((e,t)=>P(t,h)&&ll(e.cells)?x:(e,o,n)=>!("th"===ne(e.element)&&t[n]))(i,d),p=((e,t)=>(o,n,r)=>C.some(Jl(e,o.element,"col",t[r])))(n,d);return Xl(f,m,r,s,l.transformCell,p,g)})(o,c,e,t,r,s.replaceOrInit,l);return Ta(i,n[0].row,n[0].column)},Oa=Da("thead",!0),ka=Da("tbody",!1),Ea=Da("tfoot",!1),Na=(e,t,o)=>{const n=((e,t)=>Jt(e,(()=>t)))(e,o.section),r=Zo(n);return Tl(r,t,!0)},Ba=(e,t,o,n)=>((e,t,o,n)=>{const r=Zo(t),s=n.getWidths(r,n);ra(r,s,n)})(0,t,0,n.sizing),_a=(e,t,o,n)=>((e,t,o,n,r)=>{const s=Zo(t),l=n.getWidths(s,n),a=n.pixelWidth(),{newSizes:c,delta:i}=r.calcRedestributedWidths(l,a,o.pixelDelta,n.isRelative);ra(s,c,n),n.adjustTableWidth(i)})(0,t,o,n.sizing,n.resize),za=(e,t)=>O(t,(e=>0===e.column&&e.isLocked)),Aa=(e,t)=>O(t,(t=>t.column+t.colspan>=e.grid.columns&&t.isLocked)),La=(e,t)=>{const o=an(e),n=aa(t);return A(n,((e,t)=>e+o[t.column].map(Lo).getOr(0)),0)},Wa=e=>(t,o)=>Nl(t,o).filter((o=>!(e?za:Aa)(t,o))).map((e=>({details:e,pixelDelta:La(t,e)}))),Ma=e=>(t,o)=>El(t,o).filter((o=>!(e?za:Aa)(t,o.cells))),ja=ga("th"),Pa=ga("td"),Ia=kl(((e,t,o,n)=>{const r=t[0].row,s=Ra(t),l=z(s,((e,t)=>({grid:Gl(e.grid,r,t.row+e.delta,o,n.getOrInit),delta:e.delta+1})),{grid:e,delta:0}).grid;return Ta(l,r,t[0].column)}),Nl,f,f,fa),Fa=kl(((e,t,o,n)=>{const r=Ra(t),s=r[r.length-1],l=s.row+s.rowspan,a=z(r,((e,t)=>Gl(e,l,t.row,o,n.getOrInit)),e);return Ta(a,l,t[0].column)}),Nl,f,f,fa),Ha=kl(((e,t,o,n)=>{const r=t.details,s=aa(r),l=s[0].column,a=z(s,((e,t)=>({grid:Kl(e.grid,l,t.column+e.delta,o,n.getOrInit),delta:e.delta+1})),{grid:e,delta:0}).grid;return Ta(a,r[0].row,l)}),Wa(!0),_a,f,fa),$a=kl(((e,t,o,n)=>{const r=t.details,s=r[r.length-1],l=s.column+s.colspan,a=aa(r),c=z(a,((e,t)=>Kl(e,l,t.column,o,n.getOrInit)),e);return Ta(c,r[0].row,l)}),Wa(!1),_a,f,fa),Va=kl(((e,t,o,n)=>{const r=aa(t.details),s=((e,t)=>j(e,(e=>{const o=e.cells,n=z(t,((e,t)=>t>=0&&t0?[tt(e.element,n,e.section,e.isNew)]:[]})))(e,E(r,(e=>e.column))),l=s.length>0?s[0].cells.length-1:0;return Ta(s,r[0].row,Math.min(r[0].column,l))}),((e,t)=>Bl(e,t).map((t=>({details:t,pixelDelta:-La(e,t)})))),_a,Ca,fa),qa=kl(((e,t,o,n)=>{const r=Ra(t),s=((e,t,o)=>{const{rows:n,cols:r}=Vo(e);return[...r,...n.slice(0,t),...n.slice(o+1)]})(e,r[0].row,r[r.length-1].row),l=Math.max(Vo(s).rows.length-1,0);return Ta(s,Math.min(t[0].row,l),t[0].column)}),Nl,f,Ca,fa),Ua=kl(((e,t,o,n)=>{const r=aa(t),s=E(r,(e=>e.column)),l=ea(e,s,!0,o,n.replaceOrInit);return Ta(l,t[0].row,t[0].column)}),Bl,f,f,ja),Ga=kl(((e,t,o,n)=>{const r=aa(t),s=E(r,(e=>e.column)),l=ea(e,s,!1,o,n.replaceOrInit);return Ta(l,t[0].row,t[0].column)}),Bl,f,f,Pa),Ka=kl(Oa,Bl,f,f,ja),Ya=kl(ka,Bl,f,f,Pa),Ja=kl(Ea,Bl,f,f,Pa),Qa=kl(((e,t,o,n)=>{const r=ta(e,t,o,n.replaceOrInit);return Ta(r,t[0].row,t[0].column)}),Bl,f,f,ja),Xa=kl(((e,t,o,n)=>{const r=ta(e,t,o,n.replaceOrInit);return Ta(r,t[0].row,t[0].column)}),Bl,f,f,Pa),Za=kl(((e,t,o,n)=>{const r=t.cells;ya(r);const s=((e,t,o,n)=>{const r=Vo(e).rows;if(0===r.length)return e;for(let e=t.startRow;e<=t.finishRow;e++)for(let o=t.startCol;o<=t.finishCol;o++){const t=r[e],s=Fo(t,o).isLocked;Po(t,o,et(n(),!1,s))}return e})(e,t.bounds,0,n.merge(r));return Sa(s,C.from(r[0]))}),((e,t)=>((e,t)=>t.mergable)(0,t).filter((t=>_l(e,t.cells)))),Ba,f,ha),ec=kl(((e,t,o,n)=>{const r=z(t,((e,t)=>zl(e,t,o,n.unmerge(t))),e);return Sa(r,C.from(t[0]))}),((e,t)=>((e,t)=>t.unmergable)(0,t).filter((t=>_l(e,t)))),Ba,f,ha),tc=kl(((e,t,o,n)=>{const r=((e,t)=>{const o=Xo(e);return Tl(o,t,!0)})(t.clipboard,t.generators);var s,l;return((e,t,o,n,r)=>{const s=Ko(t),l=((e,t,o)=>{const n=$o(t[0]),r=Vo(t).cols.length+e.row,s=k(n-e.column,(t=>t+e.column));return{row:r,column:L(s,(e=>P(o,(t=>t!==e)))).getOr(n-1)}})(e,t,s),a=Vo(o).rows,c=Vl(l,a,s),i=((e,t,o)=>{if(e.row>=t.length||e.column>$o(t[0]))return Wl.error("invalid start address out of table bounds, row: "+e.row+", column: "+e.column);const n=t.slice(e.row),r=n[0].cells.slice(e.column),s=$o(o[0]),l=o.length;return Wl.value({rowDelta:n.length-l,colDelta:r.length-s})})(l,t,a);return i.map((e=>{const o={...e,colDelta:e.colDelta-c.length},s=Hl(t,o,n),i=Ko(s),m=Vl(l,a,i);return((e,t,o,n,r,s)=>{const l=e.row,a=e.column,c=l+o.length,i=a+$o(o[0])+s.length,m=I(s,x);for(let e=l;eSa(e,C.some(t.element))),(e=>Ta(e,t.row,t.column)))}),((e,t)=>Vt(t.element).bind((o=>Dl(e,o).map((e=>({...e,generators:t.generators,clipboard:t.clipboard})))))),Ba,f,fa),oc=kl(((e,t,o,n)=>{const r=Vo(e).rows,s=t.cells[0].column,l=r[t.cells[0].row],a=Na(t.clipboard,t.generators,l),c=ql(s,e,a,t.generators,o);return Ta(c,t.cells[0].row,t.cells[0].column)}),Ma(!0),f,f,fa),nc=kl(((e,t,o,n)=>{const r=Vo(e).rows,s=t.cells[t.cells.length-1].column+t.cells[t.cells.length-1].colspan,l=r[t.cells[0].row],a=Na(t.clipboard,t.generators,l),c=ql(s,e,a,t.generators,o);return Ta(c,t.cells[0].row,s)}),Ma(!1),f,f,fa),rc=kl(((e,t,o,n)=>{const r=Vo(e).rows,s=t.cells[0].row,l=r[s],a=Na(t.clipboard,t.generators,l),c=Ul(s,e,a,t.generators,o);return Ta(c,t.cells[0].row,t.cells[0].column)}),El,f,f,fa),sc=kl(((e,t,o,n)=>{const r=Vo(e).rows,s=t.cells[t.cells.length-1].row+t.cells[t.cells.length-1].rowspan,l=r[t.cells[0].row],a=Na(t.clipboard,t.generators,l),c=Ul(s,e,a,t.generators,o);return Ta(c,s,t.cells[0].column)}),El,f,f,fa),lc=(e,t)=>{const o=Xo(e);return Nl(o,t).bind((e=>{const t=e[e.length-1],n=e[0].column,r=t.column+t.colspan,s=M(E(o.all,(e=>_(e.cells,(e=>e.column>=n&&e.column{const o=Xo(e);return Nl(o,t).bind(il).getOr("")},cc=(e,t)=>{const o=Xo(e);return Nl(o,t).bind((e=>{const t=e[e.length-1],n=e[0].row,r=t.row+t.rowspan;return(e=>{const t=E(e,(e=>cl(e).type)),o=D(t,"header"),n=D(t,"footer");if(o||n){const e=D(t,"body");return!o||e||n?o||e||!n?C.none():C.some("footer"):C.some("header")}return C.some("body")})(o.all.slice(n,r))})).getOr("")},ic=(e,t)=>e.dispatch("NewRow",{node:t}),mc=(e,t)=>e.dispatch("NewCell",{node:t}),dc=(e,t,o)=>{e.dispatch("TableModified",{...o,table:t})},uc={structure:!1,style:!0},fc={structure:!0,style:!1},gc={structure:!0,style:!0},hc=(e,t)=>Hr(e)?ur(t):$r(e)?dr(t):mr(t),pc=(e,t,o)=>{const n=e=>"table"===ne(Zr(e)),r=Wr(e),s=Ir(e)?f:Js,l=t=>{switch(Mr(e)){case"section":return hl();case"sectionCells":return pl();case"cells":return bl();default:return((e,t)=>{var o;switch((o=Xo(e),V(o.all,(e=>{const t=cl(e);return"header"===t.type?C.from(t.subType):C.none()}))).getOr(t)){case"section":return ul();case"sectionCells":return fl();case"cells":return gl()}})(t,"section")}},a=(n,s,a,c)=>(i,m,d=!1)=>{ts(i);const u=xe.fromDom(e.getDoc()),f=Br(a,u,r),g={sizing:hc(e,i),resize:Ir(e)?ol():nl(),section:l(i)};return s(i)?n(i,m,f,g).bind((n=>{t.refresh(i.dom),N(n.newRows,(t=>{ic(e,t.dom)})),N(n.newCells,(t=>{mc(e,t.dom)}));const r=((t,n)=>n.cursor.fold((()=>{const n=qt(t);return H(n).filter(lt).map((n=>{o.clearSelectedCells(t.dom);const r=e.dom.createRng();return r.selectNode(n.dom),e.selection.setRng(r),ge(n,"data-mce-selected","1"),r}))}),(n=>{const r=Ks(Ys,n),s=e.dom.createRng();return s.setStart(r.element.dom,r.offset),s.setEnd(r.element.dom,r.offset),e.selection.setRng(s),o.clearSelectedCells(t.dom),C.some(s)})))(i,n);return lt(i)&&(ts(i),d||dc(e,i.dom,c)),r.map((e=>({rng:e,effect:c})))})):C.none()},c=a(qa,(t=>!n(e)||rl(t).rows>1),f,fc),i=a(Va,(t=>!n(e)||rl(t).columns>1),f,fc);return{deleteRow:c,deleteColumn:i,insertRowsBefore:a(Ia,x,f,fc),insertRowsAfter:a(Fa,x,f,fc),insertColumnsBefore:a(Ha,x,s,fc),insertColumnsAfter:a($a,x,s,fc),mergeCells:a(Za,x,f,fc),unmergeCells:a(ec,x,f,fc),pasteColsBefore:a(oc,x,f,fc),pasteColsAfter:a(nc,x,f,fc),pasteRowsBefore:a(rc,x,f,fc),pasteRowsAfter:a(sc,x,f,fc),pasteCells:a(tc,x,f,gc),makeCellsHeader:a(Qa,x,f,fc),unmakeCellsHeader:a(Xa,x,f,fc),makeColumnsHeader:a(Ua,x,f,fc),unmakeColumnsHeader:a(Ga,x,f,fc),makeRowsHeader:a(Ka,x,f,fc),makeRowsBody:a(Ya,x,f,fc),makeRowsFooter:a(Ja,x,f,fc),getTableRowType:cc,getTableCellType:ac,getTableColType:lc}},bc=(e,t,o)=>{const n=Wt(e,t,1);1===o||n<=1?we(e,t):ge(e,t,Math.min(o,n))},wc=(e,t)=>o=>{const n=o.column+o.colspan-1,r=o.column;return n>=e&&r{const n=o.substring(0,o.length-e.length),r=parseFloat(n);return n===r.toString()?t(r):vc.invalid(o)},xc={...vc,from:e=>Rt(e,"%")?yc("%",vc.percent,e):Rt(e,"px")?yc("px",vc.pixels,e):vc.invalid(e)},Cc=(e,t,o)=>{const n=xc.from(o),r=P(e,(e=>"0px"===e))?((e,t)=>{const o=e.fold((()=>g("")),(e=>g(e/t+"px")),(()=>g(100/t+"%")));return k(t,o)})(n,e.length):((e,t,o)=>e.fold((()=>t),(e=>((e,t,o)=>{const n=o/t;return E(e,(e=>xc.from(e).fold((()=>e),(e=>e*n+"px"),(e=>e/100*o+"px"))))})(t,o,e)),(e=>((e,t)=>E(e,(e=>xc.from(e).fold((()=>e),(e=>e/t*100+"%"),(e=>e+"%")))))(t,o))))(n,e,t);return Rc(r)},Sc=(e,t)=>0===e.length?t:z(e,((e,t)=>xc.from(t).fold(g(0),h,h)+e),0),Tc=(e,t)=>xc.from(e).fold(g(e),(e=>e+t+"px"),(e=>e+t+"%")),Rc=e=>{if(0===e.length)return e;const t=z(e,((e,t)=>{const o=xc.from(t).fold((()=>({value:t,remainder:0})),(e=>((e,t)=>{const o=Math.floor(e);return{value:o+"px",remainder:e-o}})(e)),(e=>({value:e+"%",remainder:0})));return{output:[o.value].concat(e.output),remainder:e.remainder+o.remainder}}),{output:[],remainder:0}),o=t.output;return o.slice(0,o.length-1).concat([Tc(o[o.length-1],Math.round(t.remainder))])},Dc=xc.from,Oc=(e,t,o)=>{const n=Xo(e),r=n.all,s=nn(n),l=rn(n);t.each((t=>{const o=Dc(t).fold(g("px"),g("px"),g("%")),r=Ao(e),a=((e,t)=>nr(e,t,er,rr))(n,e),c=Cc(a,r,t);sn(n)?((e,t,o)=>{N(t,((t,n)=>{const r=Sc([e[n]],It());Nt(t.element,"width",r+o)}))})(c,l,o):((e,t,o)=>{N(t,(t=>{const n=e.slice(t.column,t.colspan+t.column),r=Sc(n,It());Nt(t.element,"width",r+o)}))})(c,s,o),Nt(e,"width",t)})),o.each((t=>{const o=gn(e),l=((e,t)=>lr(e,t,tr,rr))(n,e);((e,t,o)=>{N(o,(e=>{Lt(e.element,"height")})),N(t,((t,o)=>{Nt(t.element,"height",e[o])}))})(Cc(l,o,t),r,s),Nt(e,"height",t)}))},kc=e=>Un(e).exists((e=>Wn.test(e))),Ec=e=>Un(e).exists((e=>Mn.test(e))),Nc=e=>Un(e).isNone(),Bc=e=>{we(e,"width"),we(e,"height")},_c=e=>{const t=Qn(e);Oc(e,C.some(t),C.none()),Bc(e)},zc=e=>{const t=(e=>Ao(e)+"px")(e);Oc(e,C.some(t),C.none()),Bc(e)},Ac=e=>{Lt(e,"width");const t=Ut(e),o=t.length>0?t:qt(e);N(o,(e=>{Lt(e,"width"),Bc(e)})),Bc(e)},Lc={styles:{"border-collapse":"collapse",width:"100%"},attributes:{border:"1"},colGroups:!1},Wc=(e,t,o,n)=>k(e,(e=>((e,t,o,n)=>{const r=xe.fromTag("tr");for(let s=0;s{e.selection.select(t.dom,!0),e.selection.collapse(!0)},jc=(e,t,o,n,s)=>{const l=(e=>{const t=e.options,o=t.get("table_default_styles");return t.isSet("table_default_styles")?o:((e,t)=>Vr(e)||!Ur(e)?t:$r(e)?{...t,width:Lr(e)}:{...t,width:Ar})(e,o)})(e),a={styles:l,attributes:Kr(e),colGroups:Yr(e)};return e.undoManager.ignore((()=>{const r=((e,t,o,n,r,s=Lc)=>{const l=xe.fromTag("table"),a="cells"!==r;Bt(l,s.styles),he(l,s.attributes),s.colGroups&&Ie(l,(e=>{const t=xe.fromTag("colgroup");return k(e,(()=>Ie(t,xe.fromTag("col")))),t})(t));const c=Math.min(e,o);if(a&&o>0){const e=xe.fromTag("thead");Ie(l,e);const s=Wc(o,t,"sectionCells"===r?c:0,n);$e(e,s)}const i=xe.fromTag("tbody");Ie(l,i);const m=Wc(a?e-c:e,t,a?0:o,n);return $e(i,m),l})(o,t,s,n,Mr(e),a);ge(r,"data-mce-id","__mce");const l=(e=>{const t=xe.fromTag("div"),o=xe.fromDom(e.dom.cloneNode(!0));return Ie(t,o),(e=>e.dom.innerHTML)(t)})(r);e.insertContent(l),e.addVisual()})),bt(Zr(e),'table[data-mce-id="__mce"]').map((t=>($r(e)?zc(t):Vr(e)?Ac(t):(Hr(e)||(e=>r(e)&&-1!==e.indexOf("%"))(l.width))&&_c(t),ts(t),we(t,"data-mce-id"),((e,t)=>{N(dt(t,"tr"),(t=>{ic(e,t.dom),N(dt(t,"th,td"),(t=>{mc(e,t.dom)}))}))})(e,t),((e,t)=>{bt(t,"td,th").each(b(Mc,e))})(e,t),t.dom))).getOrNull()};var Pc=tinymce.util.Tools.resolve("tinymce.FakeClipboard");const Ic="x-tinymce/dom-table-",Fc=Ic+"rows",Hc=Ic+"columns",$c=e=>{const t=Pc.FakeClipboardItem(e);Pc.write([t])},Vc=e=>{var t;const o=null!==(t=Pc.read())&&void 0!==t?t:[];return V(o,(t=>C.from(t.getType(e))))},qc=e=>{Vc(e).isSome()&&Pc.clear()},Uc=e=>{e.fold(Kc,(e=>$c({[Fc]:e})))},Gc=()=>Vc(Fc),Kc=()=>qc(Fc),Yc=e=>{e.fold(Qc,(e=>$c({[Hc]:e})))},Jc=()=>Vc(Hc),Qc=()=>qc(Hc),Xc=e=>Is(os(e),es(e)).filter(cs),Zc=(e,t)=>{const o=es(e),n=e=>Gt(e,o),l=t=>(e=>Fs(os(e),es(e)).filter(cs))(e).bind((e=>n(e).map((o=>t(o,e))))),a=t=>{e.focus()},c=(t,o=!1)=>l(((n,r)=>{const s=js(Hs(e),n,r);t(n,s,o).each(a)})),i=()=>l(((t,o)=>((e,t,o)=>{const n=Xo(e);return Nl(n,t).bind((e=>{const t=Tl(n,o,!1),r=Vo(t).rows.slice(e[0].row,e[e.length-1].row+e[e.length-1].rowspan),s=j(r,(e=>{const t=_(e.cells,(e=>!e.isLocked));return t.length>0?[{...e,cells:t}]:[]})),l=Rl(s);return xt(l.length>0,l)})).map((e=>E(e,(e=>{const t=Ke(e.element);return N(e.cells,(e=>{const o=Ye(e.element);vl(o,"colspan",e.colspan,1),vl(o,"rowspan",e.rowspan,1),Ie(t,o)})),t}))))})(t,js(Hs(e),t,o),Br(f,xe.fromDom(e.getDoc()),C.none())))),m=()=>l(((t,o)=>((e,t)=>{const o=Xo(e);return Bl(o,t).map((e=>{const t=e[e.length-1],n=e[0].column,r=t.column+t.colspan,s=((e,t,o)=>{if(sn(e)){const n=_(rn(e),wc(t,o)),r=E(n,(e=>{const n=Ye(e.element);return bc(n,"span",o-t),n})),s=xe.fromTag("colgroup");return $e(s,r),[s]}return[]})(o,n,r),l=((e,t,o)=>E(e.all,(e=>{const n=_(e.cells,wc(t,o)),r=E(n,(e=>{const n=Ye(e.element);return bc(n,"colspan",o-t),n})),s=xe.fromTag("tr");return $e(s,r),s})))(o,n,r);return[...s,...l]}))})(t,js(Hs(e),t,o)))),d=(t,o)=>o().each((o=>{const n=E(o,(e=>Ye(e)));l(((o,r)=>{const s=_r(xe.fromDom(e.getDoc())),l=((e,t,o,n)=>({selection:Ns(e),clipboard:o,generators:n}))(Hs(e),0,n,s);t(o,l).each(a)}))})),g=e=>(t,o)=>((e,t)=>X(e,t)?C.from(e[t]):C.none())(o,"type").each((t=>{c(e(t),o.no_events)}));G({mceTableSplitCells:()=>c(t.unmergeCells),mceTableMergeCells:()=>c(t.mergeCells),mceTableInsertRowBefore:()=>c(t.insertRowsBefore),mceTableInsertRowAfter:()=>c(t.insertRowsAfter),mceTableInsertColBefore:()=>c(t.insertColumnsBefore),mceTableInsertColAfter:()=>c(t.insertColumnsAfter),mceTableDeleteCol:()=>c(t.deleteColumn),mceTableDeleteRow:()=>c(t.deleteRow),mceTableCutCol:()=>m().each((e=>{Yc(e),c(t.deleteColumn)})),mceTableCutRow:()=>i().each((e=>{Uc(e),c(t.deleteRow)})),mceTableCopyCol:()=>m().each((e=>Yc(e))),mceTableCopyRow:()=>i().each((e=>Uc(e))),mceTablePasteColBefore:()=>d(t.pasteColsBefore,Jc),mceTablePasteColAfter:()=>d(t.pasteColsAfter,Jc),mceTablePasteRowBefore:()=>d(t.pasteRowsBefore,Gc),mceTablePasteRowAfter:()=>d(t.pasteRowsAfter,Gc),mceTableDelete:()=>Xc(e).each((t=>{Gt(t,o).filter(w(o)).each((t=>{const o=xe.fromText("");if(je(t,o),qe(t),e.dom.isEmpty(e.getBody()))e.setContent(""),e.selection.setCursorLocation();else{const t=e.dom.createRng();t.setStart(o.dom,0),t.setEnd(o.dom,0),e.selection.setRng(t),e.nodeChanged()}}))})),mceTableCellToggleClass:(t,o)=>{l((t=>{const n=Hs(e),r=P(n,(t=>e.formatter.match("tablecellclass",{value:o},t.dom))),s=r?e.formatter.remove:e.formatter.apply;N(n,(e=>s("tablecellclass",{value:o},e.dom))),dc(e,t.dom,uc)}))},mceTableToggleClass:(t,o)=>{l((t=>{e.formatter.toggle("tableclass",{value:o},t.dom),dc(e,t.dom,uc)}))},mceTableToggleCaption:()=>{Xc(e).each((t=>{Gt(t,o).each((o=>{pt(o,"caption").fold((()=>{const t=xe.fromTag("caption");Ie(t,xe.fromText("Caption")),((e,t,o)=>{We(e,0).fold((()=>{Ie(e,t)}),(e=>{Me(e,t)}))})(o,t),e.selection.setCursorLocation(t.dom,0)}),(n=>{ue("caption")(t)&&Te("td",o).each((t=>e.selection.setCursorLocation(t.dom,0))),qe(n)})),dc(e,o.dom,fc)}))}))},mceTableSizingMode:(t,n)=>(t=>Xc(e).each((n=>{Vr(e)||$r(e)||Hr(e)||Gt(n,o).each((o=>{"relative"!==t||kc(o)?"fixed"!==t||Ec(o)?"responsive"!==t||Nc(o)||Ac(o):zc(o):_c(o),ts(o),dc(e,o.dom,fc)}))})))(n),mceTableCellType:g((e=>"th"===e?t.makeCellsHeader:t.unmakeCellsHeader)),mceTableColType:g((e=>"th"===e?t.makeColumnsHeader:t.unmakeColumnsHeader)),mceTableRowType:g((e=>{switch(e){case"header":return t.makeRowsHeader;case"footer":return t.makeRowsFooter;default:return t.makeRowsBody}}))},((t,o)=>e.addCommand(o,t))),e.addCommand("mceInsertTable",((t,o)=>{((e,t,o,n={})=>{const r=e=>u(e)&&e>0;if(r(t)&&r(o)){const r=n.headerRows||0,s=n.headerColumns||0;return jc(e,o,t,s,r)}console.error("Invalid values for mceInsertTable - rows and columns values are required to insert a table.")})(e,o.rows,o.columns,o.options)})),e.addCommand("mceTableApplyCellStyle",((t,o)=>{const l=e=>"tablecell"+e.toLowerCase().replace("-","");if(!s(o))return;const a=_(Hs(e),cs);if(0===a.length)return;const c=((e,t)=>{const o={};return((e,t,o,n)=>{G(e,((e,r)=>{(t(e,r)?o:n)(e,r)}))})(e,t,(e=>(t,o)=>{e[o]=t})(o),f),o})(o,((t,o)=>e.formatter.has(l(o))&&r(t)));(e=>{for(const t in e)if(U.call(e,t))return!1;return!0})(c)||(G(c,((t,o)=>{const n=l(o);N(a,(o=>{""===t?e.formatter.remove(n,{value:null},o.dom,!0):e.formatter.apply(n,{value:t},o.dom)}))})),n(a[0]).each((t=>dc(e,t.dom,uc))))}))},ei=oa([{before:["element"]},{on:["element","offset"]},{after:["element"]}]),ti={before:ei.before,on:ei.on,after:ei.after,cata:(e,t,o,n)=>e.fold(t,o,n),getStart:e=>e.fold(h,h,h)},oi=(e,t)=>({selection:e,kill:t}),ni=(e,t)=>{const o=e.document.createRange();return o.selectNode(t.dom),o},ri=(e,t)=>{const o=e.document.createRange();return si(o,t),o},si=(e,t)=>e.selectNodeContents(t.dom),li=(e,t,o)=>{const n=e.document.createRange();var r;return r=n,t.fold((e=>{r.setStartBefore(e.dom)}),((e,t)=>{r.setStart(e.dom,t)}),(e=>{r.setStartAfter(e.dom)})),((e,t)=>{t.fold((t=>{e.setEndBefore(t.dom)}),((t,o)=>{e.setEnd(t.dom,o)}),(t=>{e.setEndAfter(t.dom)}))})(n,o),n},ai=(e,t,o,n,r)=>{const s=e.document.createRange();return s.setStart(t.dom,o),s.setEnd(n.dom,r),s},ci=e=>({left:e.left,top:e.top,right:e.right,bottom:e.bottom,width:e.width,height:e.height}),ii=oa([{ltr:["start","soffset","finish","foffset"]},{rtl:["start","soffset","finish","foffset"]}]),mi=(e,t,o)=>t(xe.fromDom(o.startContainer),o.startOffset,xe.fromDom(o.endContainer),o.endOffset),di=(e,t)=>{const o=((e,t)=>t.match({domRange:e=>({ltr:g(e),rtl:C.none}),relative:(t,o)=>({ltr:Zt((()=>li(e,t,o))),rtl:Zt((()=>C.some(li(e,o,t))))}),exact:(t,o,n,r)=>({ltr:Zt((()=>ai(e,t,o,n,r))),rtl:Zt((()=>C.some(ai(e,n,r,t,o))))})}))(e,t);return((e,t)=>{const o=t.ltr();return o.collapsed?t.rtl().filter((e=>!1===e.collapsed)).map((e=>ii.rtl(xe.fromDom(e.endContainer),e.endOffset,xe.fromDom(e.startContainer),e.startOffset))).getOrThunk((()=>mi(0,ii.ltr,o))):mi(0,ii.ltr,o)})(0,o)},ui=(e,t)=>di(e,t).match({ltr:(t,o,n,r)=>{const s=e.document.createRange();return s.setStart(t.dom,o),s.setEnd(n.dom,r),s},rtl:(t,o,n,r)=>{const s=e.document.createRange();return s.setStart(n.dom,r),s.setEnd(t.dom,o),s}});ii.ltr,ii.rtl;const fi=(e,t,o,n)=>({start:e,soffset:t,finish:o,foffset:n}),gi=(e,t,o,n)=>({start:ti.on(e,t),finish:ti.on(o,n)}),hi=(e,t)=>{const o=ui(e,t);return fi(xe.fromDom(o.startContainer),o.startOffset,xe.fromDom(o.endContainer),o.endOffset)},pi=gi,bi=(e,t,o,n,r)=>Re(o,n)?C.none():Ts(o,n,t).bind((t=>{const n=t.boxes.getOr([]);return n.length>1?(r(e,n,t.start,t.finish),C.some(oi(C.some(pi(o,0,o,wr(o))),!0))):C.none()})),wi=(e,t)=>({item:e,mode:t}),vi=(e,t,o,n=yi)=>e.property().parent(t).map((e=>wi(e,n))),yi=(e,t,o,n=xi)=>o.sibling(e,t).map((e=>wi(e,n))),xi=(e,t,o,n=xi)=>{const r=e.property().children(t);return o.first(r).map((e=>wi(e,n)))},Ci=[{current:vi,next:yi,fallback:C.none()},{current:yi,next:xi,fallback:C.some(vi)},{current:xi,next:xi,fallback:C.some(yi)}],Si=(e,t,o,n,r=Ci)=>L(r,(e=>e.current===o)).bind((o=>o.current(e,t,n,o.next).orThunk((()=>o.fallback.bind((o=>Si(e,t,o,n))))))),Ti=(e,t,o,n,r,s)=>Si(e,t,n,r).bind((t=>s(t.item)?C.none():o(t.item)?C.some(t.item):Ti(e,t.item,o,t.mode,r,s))),Ri=e=>t=>0===e.property().children(t).length,Di=(e,t,o,n)=>Ti(e,t,o,yi,{sibling:(e,t)=>e.query().prevSibling(t),first:e=>e.length>0?C.some(e[e.length-1]):C.none()},n),Oi=(e,t,o,n)=>Ti(e,t,o,yi,{sibling:(e,t)=>e.query().nextSibling(t),first:e=>e.length>0?C.some(e[0]):C.none()},n),ki=ws(),Ei=(e,t)=>((e,t,o)=>Di(e,t,Ri(e),o))(ki,e,t),Ni=(e,t)=>((e,t,o)=>Oi(e,t,Ri(e),o))(ki,e,t),Bi=oa([{none:["message"]},{success:[]},{failedUp:["cell"]},{failedDown:["cell"]}]),_i=e=>wt(e,"tr"),zi={...Bi,verify:(e,t,o,n,r,s,l)=>wt(n,"td,th",l).bind((o=>wt(t,"td,th",l).map((t=>Re(o,t)?Re(n,o)&&wr(o)===r?s(t):Bi.none("in same cell"):Cs(_i,[o,t]).fold((()=>((e,t,o)=>{const n=e.getRect(t),r=e.getRect(o);return r.right>n.left&&r.lefts(t))))))).getOr(Bi.none("default")),cata:(e,t,o,n,r)=>e.fold(t,o,n,r)},Ai=ue("br"),Li=(e,t,o)=>t(e,o).bind((e=>ie(e)&&0===hr(e).trim().length?Li(e,t,o):C.some(e))),Wi=(e,t,o,n)=>((e,t)=>We(e,t).filter(Ai).orThunk((()=>We(e,t-1).filter(Ai))))(t,o).bind((t=>n.traverse(t).fold((()=>Li(t,n.gather,e).map(n.relative)),(e=>(e=>Ne(e).bind((t=>{const o=Le(t);return((e,t)=>W(e,b(Re,t)))(o,e).map((n=>((e,t,o,n)=>({parent:e,children:t,element:o,index:n}))(t,o,e,n)))})))(e).map((e=>ti.on(e.parent,e.index))))))),Mi=(e,t)=>({left:e.left,top:e.top+t,right:e.right,bottom:e.bottom+t}),ji=(e,t)=>({left:e.left,top:e.top-t,right:e.right,bottom:e.bottom-t}),Pi=(e,t,o)=>({left:e.left+t,top:e.top+o,right:e.right+t,bottom:e.bottom+o}),Ii=e=>({left:e.left,top:e.top,right:e.right,bottom:e.bottom}),Fi=(e,t)=>C.some(e.getRect(t)),Hi=(e,t,o)=>ce(t)?Fi(e,t).map(Ii):ie(t)?((e,t,o)=>o>=0&&o0?e.getRangedRect(t,o-1,t,o):C.none())(e,t,o).map(Ii):C.none(),$i=(e,t)=>ce(t)?Fi(e,t).map(Ii):ie(t)?e.getRangedRect(t,0,t,wr(t)).map(Ii):C.none(),Vi=oa([{none:[]},{retry:["caret"]}]),qi=(e,t,o)=>gt(t,wa).fold(y,(t=>$i(e,t).exists((e=>((e,t)=>e.leftt.right)(o,e))))),Ui={point:e=>e.bottom,adjuster:(e,t,o,n,r)=>{const s=Mi(r,5);return Math.abs(o.bottom-n.bottom)<1||o.top>r.bottom?Vi.retry(s):o.top===r.bottom?Vi.retry(Mi(r,1)):qi(e,t,r)?Vi.retry(Pi(s,5,0)):Vi.none()},move:Mi,gather:Ni},Gi=(e,t,o,n,r)=>0===r?C.some(n):((e,t,o)=>e.elementFromPoint(t,o).filter((e=>"table"===ne(e))).isSome())(e,n.left,t.point(n))?((e,t,o,n,r)=>Gi(e,t,o,t.move(n,5),r))(e,t,o,n,r-1):e.situsFromPoint(n.left,t.point(n)).bind((s=>s.start.fold(C.none,(s=>$i(e,s).bind((l=>t.adjuster(e,s,l,o,n).fold(C.none,(n=>Gi(e,t,o,n,r-1))))).orThunk((()=>C.some(n)))),C.none))),Ki=(e,t,o)=>{const n=e.move(o,5),r=Gi(t,e,o,n,100).getOr(n);return((e,t,o)=>e.point(t)>o.getInnerHeight()?C.some(e.point(t)-o.getInnerHeight()):e.point(t)<0?C.some(-e.point(t)):C.none())(e,r,t).fold((()=>t.situsFromPoint(r.left,e.point(r))),(o=>(t.scrollBy(0,o),t.situsFromPoint(r.left,e.point(r)-o))))},Yi={tryUp:b(Ki,{point:e=>e.top,adjuster:(e,t,o,n,r)=>{const s=ji(r,5);return Math.abs(o.top-n.top)<1||o.bottome.getSelection().bind((n=>((e,t,o,n)=>{const r=Ai(t)?((e,t,o)=>o.traverse(t).orThunk((()=>Li(t,o.gather,e))).map(o.relative))(e,t,n):Wi(e,t,o,n);return r.map((e=>({start:e,finish:e})))})(t,n.finish,n.foffset,o).fold((()=>C.some(Vs(n.finish,n.foffset))),(r=>{const s=e.fromSitus(r);return l=zi.verify(e,n.finish,n.foffset,s.finish,s.foffset,o.failure,t),zi.cata(l,(e=>C.none()),(()=>C.none()),(e=>C.some(Vs(e,0))),(e=>C.some(Vs(e,wr(e)))));var l})))),Qi=(e,t,o,n,r,s)=>0===s?C.none():em(e,t,o,n,r).bind((l=>{const a=e.fromSitus(l),c=zi.verify(e,o,n,a.finish,a.foffset,r.failure,t);return zi.cata(c,(()=>C.none()),(()=>C.some(l)),(l=>Re(o,l)&&0===n?Xi(e,o,n,ji,r):Qi(e,t,l,0,r,s-1)),(l=>Re(o,l)&&n===wr(l)?Xi(e,o,n,Mi,r):Qi(e,t,l,wr(l),r,s-1)))})),Xi=(e,t,o,n,r)=>Hi(e,t,o).bind((t=>Zi(e,r,n(t,Yi.getJumpSize())))),Zi=(e,t,o)=>{const n=No().browser;return n.isChromium()||n.isSafari()||n.isFirefox()?t.retry(e,o):C.none()},em=(e,t,o,n,r)=>Hi(e,o,n).bind((t=>Zi(e,r,t))),tm=(e,t,o,n,r)=>wt(n,"td,th",t).bind((n=>wt(n,"table",t).bind((s=>((e,t)=>ft(e,(e=>Ne(e).exists((e=>Re(e,t)))),void 0).isSome())(r,s)?((e,t,o)=>Ji(e,t,o).bind((n=>Qi(e,t,n.element,n.offset,o,20).map(e.fromSitus))))(e,t,o).bind((e=>wt(e.finish,"td,th",t).map((t=>({start:n,finish:t,range:e}))))):C.none())))),om=(e,t,o,n,r,s)=>s(n,t).orThunk((()=>tm(e,t,o,n,r).map((e=>{const t=e.range;return oi(C.some(pi(t.start,t.soffset,t.finish,t.foffset)),!0)})))),nm=(e,t)=>wt(e,"tr",t).bind((e=>wt(e,"table",t).bind((o=>{const n=dt(o,"tr");return Re(e,n[0])?((e,t,o)=>Di(ki,e,(e=>Cr(e).isSome()),o))(o,0,t).map((e=>{const t=wr(e);return oi(C.some(pi(e,t,e,t)),!0)})):C.none()})))),rm=(e,t)=>wt(e,"tr",t).bind((e=>wt(e,"table",t).bind((o=>{const n=dt(o,"tr");return Re(e,n[n.length-1])?((e,t,o)=>Oi(ki,e,(e=>xr(e).isSome()),o))(o,0,t).map((e=>oi(C.some(pi(e,0,e,0)),!0))):C.none()})))),sm=(e,t,o,n,r,s,l)=>tm(e,o,n,r,s).bind((e=>bi(t,o,e.start,e.finish,l))),lm=e=>{let t=e;return{get:()=>t,set:e=>{t=e}}},am=()=>{const e=(e=>{const t=lm(C.none()),o=()=>t.get().each(e);return{clear:()=>{o(),t.set(C.none())},isSet:()=>t.get().isSome(),get:()=>t.get(),set:e=>{o(),t.set(C.some(e))}}})(f);return{...e,on:t=>e.get().each(t)}},cm=(e,t)=>wt(e,"td,th",t),im=e=>Be(e).exists(Qr),mm={traverse:Ae,gather:Ni,relative:ti.before,retry:Yi.tryDown,failure:zi.failedDown},dm={traverse:ze,gather:Ei,relative:ti.before,retry:Yi.tryUp,failure:zi.failedUp},um=e=>t=>t===e,fm=um(38),gm=um(40),hm=e=>e>=37&&e<=40,pm={isBackward:um(37),isForward:um(39)},bm={isBackward:um(39),isForward:um(37)},wm=oa([{domRange:["rng"]},{relative:["startSitu","finishSitu"]},{exact:["start","soffset","finish","foffset"]}]),vm={domRange:wm.domRange,relative:wm.relative,exact:wm.exact,exactFromRange:e=>wm.exact(e.start,e.soffset,e.finish,e.foffset),getWin:e=>{const t=(e=>e.match({domRange:e=>xe.fromDom(e.startContainer),relative:(e,t)=>ti.getStart(e),exact:(e,t,o,n)=>e}))(e);return xe.fromDom(Ee(t).dom.defaultView)},range:fi},ym=document.caretPositionFromPoint?(e,t,o)=>{var n,r;return C.from(null===(r=(n=e.dom).caretPositionFromPoint)||void 0===r?void 0:r.call(n,t,o)).bind((t=>{if(null===t.offsetNode)return C.none();const o=e.dom.createRange();return o.setStart(t.offsetNode,t.offset),o.collapse(),C.some(o)}))}:document.caretRangeFromPoint?(e,t,o)=>{var n,r;return C.from(null===(r=(n=e.dom).caretRangeFromPoint)||void 0===r?void 0:r.call(n,t,o))}:C.none,xm=(e,t)=>{const o=ne(e);return"input"===o?ti.after(e):D(["br","img"],o)?0===t?ti.before(e):ti.after(e):ti.on(e,t)},Cm=e=>C.from(e.getSelection()),Sm=(e,t)=>{Cm(e).each((e=>{e.removeAllRanges(),e.addRange(t)}))},Tm=(e,t,o,n,r)=>{const s=ai(e,t,o,n,r);Sm(e,s)},Rm=(e,t)=>di(e,t).match({ltr:(t,o,n,r)=>{Tm(e,t,o,n,r)},rtl:(t,o,n,r)=>{Cm(e).each((s=>{if(s.setBaseAndExtent)s.setBaseAndExtent(t.dom,o,n.dom,r);else if(s.extend)try{((e,t,o,n,r,s)=>{t.collapse(o.dom,n),t.extend(r.dom,s)})(0,s,t,o,n,r)}catch(s){Tm(e,n,r,t,o)}else Tm(e,n,r,t,o)}))}}),Dm=(e,t,o,n,r)=>{const s=((e,t,o,n)=>{const r=xm(e,t),s=xm(o,n);return vm.relative(r,s)})(t,o,n,r);Rm(e,s)},Om=(e,t,o)=>{const n=((e,t)=>{const o=e.fold(ti.before,xm,ti.after),n=t.fold(ti.before,xm,ti.after);return vm.relative(o,n)})(t,o);Rm(e,n)},km=e=>{if(e.rangeCount>0){const t=e.getRangeAt(0),o=e.getRangeAt(e.rangeCount-1);return C.some(fi(xe.fromDom(t.startContainer),t.startOffset,xe.fromDom(o.endContainer),o.endOffset))}return C.none()},Em=e=>{if(null===e.anchorNode||null===e.focusNode)return km(e);{const t=xe.fromDom(e.anchorNode),o=xe.fromDom(e.focusNode);return((e,t,o,n)=>{const r=((e,t,o,n)=>{const r=ke(e).dom.createRange();return r.setStart(e.dom,t),r.setEnd(o.dom,n),r})(e,t,o,n),s=Re(e,o)&&t===n;return r.collapsed&&!s})(t,e.anchorOffset,o,e.focusOffset)?C.some(fi(t,e.anchorOffset,o,e.focusOffset)):km(e)}},Nm=(e,t,o=!0)=>{const n=(o?ri:ni)(e,t);Sm(e,n)},Bm=e=>(e=>Cm(e).filter((e=>e.rangeCount>0)).bind(Em))(e).map((e=>vm.exact(e.start,e.soffset,e.finish,e.foffset))),_m=e=>({elementFromPoint:(t,o)=>xe.fromPoint(xe.fromDom(e.document),t,o),getRect:e=>e.dom.getBoundingClientRect(),getRangedRect:(t,o,n,r)=>{const s=vm.exact(t,o,n,r);return((e,t)=>(e=>{const t=e.getClientRects(),o=t.length>0?t[0]:e.getBoundingClientRect();return o.width>0||o.height>0?C.some(o).map(ci):C.none()})(ui(e,t)))(e,s)},getSelection:()=>Bm(e).map((t=>hi(e,t))),fromSitus:t=>{const o=vm.relative(t.start,t.finish);return hi(e,o)},situsFromPoint:(t,o)=>((e,t,o)=>((e,t,o)=>{const n=xe.fromDom(e.document);return ym(n,t,o).map((e=>fi(xe.fromDom(e.startContainer),e.startOffset,xe.fromDom(e.endContainer),e.endOffset)))})(e,t,o))(e,t,o).map((e=>gi(e.start,e.soffset,e.finish,e.foffset))),clearSelection:()=>{(e=>{Cm(e).each((e=>e.removeAllRanges()))})(e)},collapseSelection:(t=!1)=>{Bm(e).each((o=>o.fold((e=>e.collapse(t)),((o,n)=>{const r=t?o:n;Om(e,r,r)}),((o,n,r,s)=>{const l=t?o:r,a=t?n:s;Dm(e,l,a,l,a)}))))},setSelection:t=>{Dm(e,t.start,t.soffset,t.finish,t.foffset)},setRelativeSelection:(t,o)=>{Om(e,t,o)},selectNode:t=>{Nm(e,t,!1)},selectContents:t=>{Nm(e,t)},getInnerHeight:()=>e.innerHeight,getScrollY:()=>(e=>{const t=void 0!==e?e.dom:document,o=t.body.scrollLeft||t.documentElement.scrollLeft,n=t.body.scrollTop||t.documentElement.scrollTop;return bn(o,n)})(xe.fromDom(e.document)).top,scrollBy:(t,o)=>{((e,t,o)=>{const n=(void 0!==o?o.dom:document).defaultView;n&&n.scrollBy(e,t)})(t,o,xe.fromDom(e.document))}}),zm=(e,t)=>({rows:e,cols:t}),Am=e=>gt(e,ae).exists(Qr),Lm=(e,t)=>Am(e)||Am(t),Wm=e=>void 0!==e.dom.classList,Mm=(e,t)=>((e,t,o)=>{const n=((e,t)=>{const o=pe(e,t);return void 0===o||""===o?[]:o.split(" ")})(e,t).concat([o]);return ge(e,t,n.join(" ")),!0})(e,"class",t),jm=(e,t)=>{Wm(e)?e.dom.classList.add(t):Mm(e,t)},Pm=(e,t)=>Wm(e)&&e.dom.classList.contains(t),Im=()=>({tag:"none"}),Fm=e=>({tag:"multiple",elements:e}),Hm=e=>({tag:"single",element:e}),$m=e=>{const t=xe.fromDom((e=>{if(nt()&&m(e.target)){const t=xe.fromDom(e.target);if(ce(t)&&m(t.dom.shadowRoot)&&e.composed&&e.composedPath){const t=e.composedPath();if(t)return H(t)}}return C.from(e.target)})(e).getOr(e.target)),o=()=>e.stopPropagation(),n=()=>e.preventDefault(),r=(s=n,l=o,(...e)=>s(l.apply(null,e)));var s,l;return((e,t,o,n,r,s,l)=>({target:e,x:t,y:o,stop:n,prevent:r,kill:s,raw:l}))(t,e.clientX,e.clientY,o,n,r,e)},Vm=(e,t,o,n)=>{e.dom.removeEventListener(t,o,n)},qm=x,Um=(e,t,o)=>((e,t,o,n)=>((e,t,o,n,r)=>{const s=((e,t)=>o=>{e(o)&&t($m(o))})(o,n);return e.dom.addEventListener(t,s,r),{unbind:b(Vm,e,t,s,r)}})(e,t,o,n,!1))(e,t,qm,o),Gm=$m,Km=e=>!Pm(xe.fromDom(e.target),"ephox-snooker-resizer-bar"),Ym=(e,t)=>{const o=(r=Ms.selectedSelector,{get:()=>ks(xe.fromDom(e.getBody()),r).fold((()=>Fs(os(e),es(e)).fold(Im,Hm)),Fm)}),n=((e,t,o)=>{const n=t=>{we(t,e.selected),we(t,e.firstSelected),we(t,e.lastSelected)},r=t=>{ge(t,e.selected,"1")},s=e=>{l(e),o()},l=t=>{const o=dt(t,`${e.selectedSelector},${e.firstSelectedSelector},${e.lastSelectedSelector}`);N(o,n)};return{clearBeforeUpdate:l,clear:s,selectRange:(o,n,l,a)=>{s(o),N(n,r),ge(l,e.firstSelected,"1"),ge(a,e.lastSelected,"1"),t(n,l,a)},selectedSelector:e.selectedSelector,firstSelectedSelector:e.firstSelectedSelector,lastSelectedSelector:e.lastSelectedSelector}})(Ms,((t,o,n)=>{Gt(o).each((r=>{const s=E(t,(e=>e.dom)),l=Wr(e),a=Br(f,xe.fromDom(e.getDoc()),l),c=((e,t,o)=>{const n=Xo(e);return Nl(n,t).map((e=>{const t=Tl(n,o,!1),{rows:r}=Vo(t),s=((e,t)=>{const o=e.slice(0,t[t.length-1].row+1),n=Rl(o);return j(n,(e=>{const o=e.cells.slice(0,t[t.length-1].column+1);return E(o,(e=>e.element))}))})(r,e),l=((e,t)=>{const o=e.slice(t[0].row+t[0].rowspan-1,e.length),n=Rl(o);return j(n,(e=>{const o=e.cells.slice(t[0].column+t[0].colspan-1,e.cells.length);return E(o,(e=>e.element))}))})(r,e);return{upOrLeftCells:s,downOrRightCells:l}}))})(r,{selection:Hs(e)},a).map((e=>K(e,(e=>E(e,(e=>e.dom)))))).getOrUndefined();((e,t,o,n,r)=>{e.dispatch("TableSelectionChange",{cells:t,start:o,finish:n,otherCells:r})})(e,s,o.dom,n.dom,c)}))}),(()=>(e=>{e.dispatch("TableSelectionClear")})(e)));var r;return e.on("init",(o=>{const r=e.getWin(),s=Zr(e),l=es(e),a=((e,t,o,n)=>{const r=((e,t,o,n)=>{const r=am(),s=r.clear,l=s=>{r.on((r=>{n.clearBeforeUpdate(t),cm(s.target,o).each((l=>{Ts(r,l,o).each((o=>{const r=o.boxes.getOr([]);if(1===r.length){const e=r[0],o="false"===Xr(e),l=vt(Jr(s.target),e,Re);o&&l&&n.selectRange(t,r,e,e)}else r.length>1&&(n.selectRange(t,r,o.start,o.finish),e.selectContents(l))}))}))}))};return{clearstate:s,mousedown:e=>{n.clear(t),cm(e.target,o).filter(im).each(r.set)},mouseover:e=>{l(e)},mouseup:e=>{l(e),s()}}})(_m(e),t,o,n);return{clearstate:r.clearstate,mousedown:r.mousedown,mouseover:r.mouseover,mouseup:r.mouseup}})(r,s,l,n),c=((e,t,o,n)=>{const r=_m(e),s=()=>(n.clear(t),C.none());return{keydown:(e,l,a,c,i,m)=>{const d=e.raw,u=d.which,f=!0===d.shiftKey,g=Rs(t,n.selectedSelector).fold((()=>(hm(u)&&!f&&n.clearBeforeUpdate(t),hm(u)&&f&&!Lm(l,c)?C.none:gm(u)&&f?b(sm,r,t,o,mm,c,l,n.selectRange):fm(u)&&f?b(sm,r,t,o,dm,c,l,n.selectRange):gm(u)?b(om,r,o,mm,c,l,rm):fm(u)?b(om,r,o,dm,c,l,nm):C.none)),(e=>{const o=o=>()=>{const s=V(o,(o=>((e,t,o,n,r)=>Os(n,e,t,r.firstSelectedSelector,r.lastSelectedSelector).map((e=>(r.clearBeforeUpdate(o),r.selectRange(o,e.boxes,e.start,e.finish),e.boxes))))(o.rows,o.cols,t,e,n)));return s.fold((()=>Ds(t,n.firstSelectedSelector,n.lastSelectedSelector).map((e=>{const o=gm(u)||m.isForward(u)?ti.after:ti.before;return r.setRelativeSelection(ti.on(e.first,0),o(e.table)),n.clear(t),oi(C.none(),!0)}))),(e=>C.some(oi(C.none(),!0))))};return hm(u)&&f&&!Lm(l,c)?C.none:gm(u)&&f?o([zm(1,0)]):fm(u)&&f?o([zm(-1,0)]):m.isBackward(u)&&f?o([zm(0,-1),zm(-1,0)]):m.isForward(u)&&f?o([zm(0,1),zm(1,0)]):hm(u)&&!f?s:C.none}));return g()},keyup:(e,r,s,l,a)=>Rs(t,n.selectedSelector).fold((()=>{const c=e.raw,i=c.which;return!0===c.shiftKey&&hm(i)&&Lm(r,l)?((e,t,o,n,r,s,l)=>Re(o,r)&&n===s?C.none():wt(o,"td,th",t).bind((o=>wt(r,"td,th",t).bind((n=>bi(e,t,o,n,l))))))(t,o,r,s,l,a,n.selectRange):C.none()}),C.none)}})(r,s,l,n),i=((e,t,o,n)=>{const r=_m(e);return(e,s)=>{n.clearBeforeUpdate(t),Ts(e,s,o).each((e=>{const o=e.boxes.getOr([]);n.selectRange(t,o,e.start,e.finish),r.selectContents(s),r.collapseSelection()}))}})(r,s,l,n);e.on("TableSelectorChange",(e=>i(e.start,e.finish)));const m=(t,o)=>{(e=>!0===e.raw.shiftKey)(t)&&(o.kill&&t.kill(),o.selection.each((t=>{const o=vm.relative(t.start,t.finish),n=ui(r,o);e.selection.setRng(n)})))},d=e=>0===e.button,u=(()=>{const e=lm(xe.fromDom(s)),t=lm(0);return{touchEnd:o=>{const n=xe.fromDom(o.target);if(ue("td")(n)||ue("th")(n)){const r=e.get(),s=t.get();Re(r,n)&&o.timeStamp-s<300&&(o.preventDefault(),i(n,n))}e.set(n),t.set(o.timeStamp)}}})();e.on("dragstart",(e=>{a.clearstate()})),e.on("mousedown",(e=>{d(e)&&Km(e)&&a.mousedown(Gm(e))})),e.on("mouseover",(e=>{var t;void 0!==(t=e).buttons&&0==(1&t.buttons)||!Km(e)||a.mouseover(Gm(e))})),e.on("mouseup",(e=>{d(e)&&Km(e)&&a.mouseup(Gm(e))})),e.on("touchend",u.touchEnd),e.on("keyup",(t=>{const o=Gm(t);if(o.raw.shiftKey&&hm(o.raw.which)){const t=e.selection.getRng(),n=xe.fromDom(t.startContainer),r=xe.fromDom(t.endContainer);c.keyup(o,n,t.startOffset,r,t.endOffset).each((e=>{m(o,e)}))}})),e.on("keydown",(o=>{const n=Gm(o);t.hide();const r=e.selection.getRng(),s=xe.fromDom(r.startContainer),l=xe.fromDom(r.endContainer),a=dn(pm,bm)(xe.fromDom(e.selection.getStart()));c.keydown(n,s,r.startOffset,l,r.endOffset,a).each((e=>{m(n,e)})),t.show()})),e.on("NodeChange",(()=>{const t=e.selection,o=xe.fromDom(t.getStart()),r=xe.fromDom(t.getEnd());Cs(Gt,[o,r]).fold((()=>n.clear(s)),f)}))})),e.on("PreInit",(()=>{e.serializer.addTempAttr(Ms.firstSelected),e.serializer.addTempAttr(Ms.lastSelected)})),{getSelectedCells:()=>((e,t,o,n)=>{switch(e.tag){case"none":return t();case"single":return(e=>[e.dom])(e.element);case"multiple":return(e=>E(e,(e=>e.dom)))(e.elements)}})(o.get(),g([])),clearSelectedCells:e=>n.clear(xe.fromDom(e))}},Jm=e=>{let t=[];return{bind:e=>{if(void 0===e)throw new Error("Event bind error: undefined handler");t.push(e)},unbind:e=>{t=_(t,(t=>t!==e))},trigger:(...o)=>{const n={};N(e,((e,t)=>{n[e]=o[t]})),N(t,(e=>{e(n)}))}}},Qm=e=>({registry:K(e,(e=>({bind:e.bind,unbind:e.unbind}))),trigger:K(e,(e=>e.trigger))}),Xm=e=>e.slice(0).sort(),Zm=(e,t)=>{const o=_(t,(t=>!D(e,t)));o.length>0&&(e=>{throw new Error("Unsupported keys for object: "+Xm(e).join(", "))})(o)},ed=e=>((e,t)=>((e,t,o)=>{if(0===t.length)throw new Error("You must specify at least one required field.");return((e,t)=>{if(!l(t))throw new Error("The "+e+" fields must be an array. Was: "+t+".");N(t,(t=>{if(!r(t))throw new Error("The value "+t+" in the "+e+" fields was not a string.")}))})("required",t),(e=>{const t=Xm(e);L(t,((e,o)=>o{throw new Error("The field: "+e+" occurs more than once in the combined fields: ["+t.join(", ")+"].")}))})(t),n=>{const r=q(n);P(t,(e=>D(r,e)))||((e,t)=>{throw new Error("All required keys ("+Xm(e).join(", ")+") were not specified. Specified keys were: "+Xm(t).join(", ")+".")})(t,r),e(t,r);const s=_(t,(e=>!o.validate(n[e],e)));return s.length>0&&((e,t)=>{throw new Error("All values need to be of type: "+t+". Keys ("+Xm(e).join(", ")+") were not.")})(s,o.label),n}})(e,t,{validate:d,label:"function"}))(Zm,e),td=ed(["compare","extract","mutate","sink"]),od=ed(["element","start","stop","destroy"]),nd=ed(["forceDrop","drop","move","delayDrop"]),rd=()=>{const e=(()=>{const e=Qm({move:Jm(["info"])});return{onEvent:f,reset:f,events:e.registry}})(),t=(()=>{let e=C.none();const t=Qm({move:Jm(["info"])});return{onEvent:(o,n)=>{n.extract(o).each((o=>{const r=((t,o)=>{const n=e.map((e=>t.compare(e,o)));return e=C.some(o),n})(n,o);r.each((e=>{t.trigger.move(e)}))}))},reset:()=>{e=C.none()},events:t.registry}})();let o=e;return{on:()=>{o.reset(),o=t},off:()=>{o.reset(),o=e},isOn:()=>o===t,onEvent:(e,t)=>{o.onEvent(e,t)},events:t.events}},sd=e=>{const t=e.replace(/\./g,"-");return{resolve:e=>t+"-"+e}},ld=sd("ephox-dragster").resolve;var ad=td({compare:(e,t)=>bn(t.left-e.left,t.top-e.top),extract:e=>C.some(bn(e.x,e.y)),sink:(e,t)=>{const o=(e=>{const t={layerClass:ld("blocker"),...e},o=xe.fromTag("div");return ge(o,"role","presentation"),Bt(o,{position:"fixed",left:"0px",top:"0px",width:"100%",height:"100%"}),jm(o,ld("blocker")),jm(o,t.layerClass),{element:g(o),destroy:()=>{qe(o)}}})(t),n=Um(o.element(),"mousedown",e.forceDrop),r=Um(o.element(),"mouseup",e.drop),s=Um(o.element(),"mousemove",e.move),l=Um(o.element(),"mouseout",e.delayDrop);return od({element:o.element,start:e=>{Ie(e,o.element())},stop:()=>{qe(o.element())},destroy:()=>{o.destroy(),r.unbind(),s.unbind(),l.unbind(),n.unbind()}})},mutate:(e,t)=>{e.mutate(t.left,t.top)}});const cd=sd("ephox-snooker").resolve,id=cd("resizer-bar"),md=cd("resizer-rows"),dd=cd("resizer-cols"),ud=e=>{const t=dt(e.parent(),"."+id);N(t,qe)},fd=(e,t,o)=>{const n=e.origin();N(t,(t=>{t.each((t=>{const r=o(n,t);jm(r,id),Ie(e.parent(),r)}))}))},gd=(e,t,o,n,r)=>{const s=vn(o),l=t.isResizable,a=n.length>0?Bn.positions(n,o):[],c=a.length>0?((e,t)=>j(e.all,((e,o)=>t(e.element)?[o]:[])))(e,l):[];((e,t,o,n)=>{fd(e,t,((e,t)=>{const r=((e,t,o,n,r)=>{const s=xe.fromTag("div");return Bt(s,{position:"absolute",left:t+"px",top:o-3.5+"px",height:"7px",width:n+"px"}),he(s,{"data-row":e,role:"presentation"}),s})(t.row,o.left-e.left,t.y-e.top,n);return jm(r,md),r}))})(t,_(a,((e,t)=>O(c,(e=>t===e)))),s,Lo(o));const i=r.length>0?zn.positions(r,o):[],m=i.length>0?((e,t)=>{const o=[];return k(e.grid.columns,(n=>{ln(e,n).map((e=>e.element)).forall(t)&&o.push(n)})),_(o,(o=>{const n=on(e,(e=>e.column===o));return P(n,(e=>t(e.element)))}))})(e,l):[];((e,t,o,n)=>{fd(e,t,((e,t)=>{const r=((e,t,o,n,r)=>{const s=xe.fromTag("div");return Bt(s,{position:"absolute",left:t-3.5+"px",top:o+"px",height:r+"px",width:"7px"}),he(s,{"data-column":e,role:"presentation"}),s})(t.col,t.x-e.left,o.top-e.top,0,n);return jm(r,dd),r}))})(t,_(i,((e,t)=>O(m,(e=>t===e)))),s,hn(o))},hd=(e,t)=>{if(ud(e),e.isResizable(t)){const o=Xo(t),n=mn(o),r=an(o);gd(o,e,t,n,r)}},pd=(e,t)=>{const o=dt(e.parent(),"."+id);N(o,t)},bd=e=>{pd(e,(e=>{Nt(e,"display","none")}))},wd=e=>{pd(e,(e=>{Nt(e,"display","block")}))},vd=cd("resizer-bar-dragging"),yd=e=>{const t=(()=>{const e=Qm({drag:Jm(["xDelta","yDelta","target"])});let t=C.none();const o=(()=>{const e=Qm({drag:Jm(["xDelta","yDelta"])});return{mutate:(t,o)=>{e.trigger.drag(t,o)},events:e.registry}})();return o.events.drag.bind((o=>{t.each((t=>{e.trigger.drag(o.xDelta,o.yDelta,t)}))})),{assign:e=>{t=C.some(e)},get:()=>t,mutate:o.mutate,events:e.registry}})(),o=((e,t={})=>{var o;return((e,t,o)=>{let n=!1;const r=Qm({start:Jm([]),stop:Jm([])}),s=rd(),l=()=>{m.stop(),s.isOn()&&(s.off(),r.trigger.stop())},c=((e,t)=>{let o=null;const n=()=>{a(o)||(clearTimeout(o),o=null)};return{cancel:n,throttle:(...t)=>{n(),o=setTimeout((()=>{o=null,e.apply(null,t)}),200)}}})(l);s.events.move.bind((o=>{t.mutate(e,o.info)}));const i=e=>(...t)=>{n&&e.apply(null,t)},m=t.sink(nd({forceDrop:l,drop:i(l),move:i((e=>{c.cancel(),s.onEvent(e,t)})),delayDrop:i(c.throttle)}),o);return{element:m.element,go:e=>{m.start(e),s.on(),r.trigger.start()},on:()=>{n=!0},off:()=>{n=!1},isActive:()=>n,destroy:()=>{m.destroy()},events:r.registry}})(e,null!==(o=t.mode)&&void 0!==o?o:ad,t)})(t,{});let n=C.none();const r=(e,t)=>C.from(pe(e,t));t.events.drag.bind((e=>{r(e.target,"data-row").each((t=>{const o=Pt(e.target,"top");Nt(e.target,"top",o+e.yDelta+"px")})),r(e.target,"data-column").each((t=>{const o=Pt(e.target,"left");Nt(e.target,"left",o+e.xDelta+"px")}))}));const s=(e,t)=>Pt(e,t)-Wt(e,"data-initial-"+t,0);o.events.stop.bind((()=>{t.get().each((t=>{n.each((o=>{r(t,"data-row").each((e=>{const n=s(t,"top");we(t,"data-initial-top"),d.trigger.adjustHeight(o,n,parseInt(e,10))})),r(t,"data-column").each((e=>{const n=s(t,"left");we(t,"data-initial-left"),d.trigger.adjustWidth(o,n,parseInt(e,10))})),hd(e,o)}))}))}));const l=(n,r)=>{d.trigger.startAdjust(),t.assign(n),ge(n,"data-initial-"+r,Pt(n,r)),jm(n,vd),Nt(n,"opacity","0.2"),o.go(e.parent())},c=Um(e.parent(),"mousedown",(e=>{var t;t=e.target,Pm(t,md)&&l(e.target,"top"),(e=>Pm(e,dd))(e.target)&&l(e.target,"left")})),i=t=>Re(t,e.view()),m=Um(e.view(),"mouseover",(t=>{var r;(r=t.target,wt(r,"table",i).filter(Qr)).fold((()=>{lt(t.target)&&ud(e)}),(t=>{o.isActive()&&(n=C.some(t),hd(e,t))}))})),d=Qm({adjustHeight:Jm(["table","delta","row"]),adjustWidth:Jm(["table","delta","column"]),startAdjust:Jm([])});return{destroy:()=>{c.unbind(),m.unbind(),o.destroy(),ud(e)},refresh:t=>{hd(e,t)},on:o.on,off:o.off,hideBars:b(bd,e),showBars:b(wd,e),events:d.registry}},xd=e=>m(e)&&"TABLE"===e.nodeName,Cd="bar-",Sd=e=>"false"!==pe(e,"data-mce-resize"),Td=e=>{const t=am(),o=am(),n=am();let r,s,l,a;const c=t=>hc(e,t),i=()=>Pr(e)?nl():ol(),m=(t,o,n,m)=>{const d=(e=>{return Tt(t=e,"corner-")?((e,t)=>e.substring(7))(t):t;var t})(o),u=Rt(d,"e"),f=Tt(d,"n");if(""===s&&_c(t),""===a&&(e=>{const t=(e=>gn(e)+"px")(e);Oc(e,C.none(),C.some(t)),Bc(e)})(t),n!==r&&""!==s){Nt(t,"width",s);const o=i(),l=c(t),a=Pr(e)||u?(e=>rl(e).columns)(t)-1:0;sa(t,n-r,a,o,l)}else if((e=>/^(\d+(\.\d+)?)%$/.test(e))(s)){const e=parseFloat(s.replace("%",""));Nt(t,"width",n*e/r+"%")}if((e=>/^(\d+(\.\d+)?)px$/.test(e))(s)&&(e=>{const t=Xo(e);sn(t)||N(qt(e),(e=>{const t=_t(e,"width");Nt(e,"width",t),we(e,"width")}))})(t),m!==l&&""!==a){Nt(t,"height",a);const e=f?0:(e=>rl(e).rows)(t)-1;la(t,m-l,e)}};return e.on("init",(()=>{const r=((e,t)=>e.inline?((e,t,o)=>({parent:g(t),view:g(e),origin:g(bn(0,0)),isResizable:o}))(xe.fromDom(e.getBody()),(()=>{const e=xe.fromTag("div");return Bt(e,{position:"static",height:"0",width:"0",padding:"0",margin:"0",border:"0"}),Ie(at(xe.fromDom(document)),e),e})(),t):((e,t)=>{const o=me(e)?(e=>xe.fromDom(Ee(e).dom.documentElement))(e):e;return{parent:g(o),view:g(e),origin:g(bn(0,0)),isResizable:t}})(xe.fromDom(e.getDoc()),t))(e,Sd);if(n.set(r),(e=>{const t=e.options.get("object_resizing");return D(t.split(","),"table")})(e)&&qr(e)){const n=((e,t,o)=>{const n=Bn,r=zn,s=yd(e),l=Qm({beforeResize:Jm(["table","type"]),afterResize:Jm(["table","type"]),startDrag:Jm([])});return s.events.adjustHeight.bind((e=>{const t=e.table;l.trigger.beforeResize(t,"row");const o=n.delta(e.delta,t);la(t,o,e.row),l.trigger.afterResize(t,"row")})),s.events.startAdjust.bind((e=>{l.trigger.startDrag()})),s.events.adjustWidth.bind((e=>{const n=e.table;l.trigger.beforeResize(n,"col");const s=r.delta(e.delta,n),a=o(n);sa(n,s,e.column,t,a),l.trigger.afterResize(n,"col")})),{on:s.on,off:s.off,refreshBars:s.refresh,hideBars:s.hideBars,showBars:s.showBars,destroy:s.destroy,events:l.registry}})(r,i(),c);n.on(),n.events.startDrag.bind((o=>{t.set(e.selection.getRng())})),n.events.beforeResize.bind((t=>{const o=t.table.dom;((e,t,o,n,r)=>{e.dispatch("ObjectResizeStart",{target:t,width:o,height:n,origin:r})})(e,o,ns(o),rs(o),Cd+t.type)})),n.events.afterResize.bind((o=>{const n=o.table,r=n.dom;ts(n),t.on((t=>{e.selection.setRng(t),e.focus()})),((e,t,o,n,r)=>{e.dispatch("ObjectResized",{target:t,width:o,height:n,origin:r})})(e,r,ns(r),rs(r),Cd+o.type),e.undoManager.add()})),o.set(n)}})),e.on("ObjectResizeStart",(t=>{const o=t.target;if(xd(o)){const n=xe.fromDom(o);N(e.dom.select(".mce-clonedresizable"),(t=>{e.dom.addClass(t,"mce-"+jr(e)+"-columns")})),!Ec(n)&&$r(e)?zc(n):!kc(n)&&Hr(e)&&_c(n),Nc(n)&&Tt(t.origin,Cd)&&_c(n),r=t.width,s=Vr(e)?"":ls(e,o).getOr(""),l=t.height,a=as(e,o).getOr("")}})),e.on("ObjectResized",(t=>{const o=t.target;if(xd(o)){const n=xe.fromDom(o),r=t.origin;(e=>Tt(e,"corner-"))(r)&&m(n,r,t.width,t.height),ts(n),dc(e,n.dom,uc)}})),e.on("SwitchMode",(()=>{o.on((t=>{e.mode.isReadOnly()?t.hideBars():t.showBars()}))})),e.on("dragstart dragend",(e=>{o.on((t=>{"dragstart"===e.type?(t.hideBars(),t.off()):(t.on(),t.showBars())}))})),e.on("remove",(()=>{o.on((e=>{e.destroy()})),n.on((t=>{((e,t)=>{e.inline&&qe(t.parent())})(e,t)}))})),{refresh:e=>{o.on((t=>t.refreshBars(xe.fromDom(e))))},hide:()=>{o.on((e=>e.hideBars()))},show:()=>{o.on((e=>e.showBars()))}}},Rd=e=>{(e=>{const t=e.options.register;t("table_clone_elements",{processor:"string[]"}),t("table_use_colgroups",{processor:"boolean",default:!0}),t("table_header_type",{processor:e=>{const t=D(["section","cells","sectionCells","auto"],e);return t?{value:e,valid:t}:{valid:!1,message:"Must be one of: section, cells, sectionCells or auto."}},default:"section"}),t("table_sizing_mode",{processor:"string",default:"auto"}),t("table_default_attributes",{processor:"object",default:{border:"1"}}),t("table_default_styles",{processor:"object",default:{"border-collapse":"collapse"}}),t("table_column_resizing",{processor:e=>{const t=D(["preservetable","resizetable"],e);return t?{value:e,valid:t}:{valid:!1,message:"Must be preservetable, or resizetable."}},default:"preservetable"}),t("table_resize_bars",{processor:"boolean",default:!0}),t("table_style_by_css",{processor:"boolean",default:!0}),t("table_merge_content_on_paste",{processor:"boolean",default:!0})})(e);const t=Td(e),o=Ym(e,t),n=pc(e,t,o);return Zc(e,n),((e,t)=>{const o=es(e),n=t=>Fs(os(e)).bind((n=>Gt(n,o).map((o=>{const r=js(Hs(e),o,n);return t(o,r)})))).getOr("");G({mceTableRowType:()=>n(t.getTableRowType),mceTableCellType:()=>n(t.getTableCellType),mceTableColType:()=>n(t.getTableColType)},((t,o)=>e.addQueryValueHandler(o,t)))})(e,n),$s(e,n),{getSelectedCells:o.getSelectedCells,clearSelectedCells:o.clearSelectedCells}};e.add("dom",(e=>({table:Rd(e)})))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/accordion/plugin.min.js b/apps/web-antd/public/tinymce/plugins/accordion/plugin.min.js new file mode 100644 index 0000000..8dc4f86 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/accordion/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");let t=0;const o=e=>t=>typeof t===e,n=e=>"string"===(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(o=n=e,(r=String).prototype.isPrototypeOf(o)||(null===(s=n.constructor)||void 0===s?void 0:s.name)===r.name)?"string":t;var o,n,r,s})(e),r=o("boolean"),s=e=>null==e,a=e=>!s(e),i=o("function"),d=o("number"),l=e=>()=>e,c=(e,t)=>e===t,m=l(!1);class u{constructor(e,t){this.tag=e,this.value=t}static some(e){return new u(!0,e)}static none(){return u.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?u.some(e(this.value)):u.none()}bind(e){return this.tag?e(this.value):u.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:u.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return a(e)?u.some(e):u.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}u.singletonNone=new u(!1);const g=Array.prototype.indexOf,p=(e,t)=>{return o=e,n=t,g.call(o,n)>-1;var o,n},h=(e,t)=>{const o=e.length,n=new Array(o);for(let r=0;r{for(let o=0,n=e.length;oe.dom.nodeName.toLowerCase(),w=e=>e.dom.nodeType,b=e=>t=>w(t)===e,N=b(1),T=b(3),A=b(9),C=b(11),S=(e,t,o)=>{if(!(n(o)||r(o)||d(o)))throw console.error("Invalid call to Attribute.set. Key ",t,":: Value ",o,":: Element ",e),new Error("Attribute value was not simple");e.setAttribute(t,o+"")},x=(e,t)=>{const o=e.dom.getAttribute(t);return null===o?void 0:o},E=(e,t)=>u.from(x(e,t)),D=(e,t)=>{e.dom.removeAttribute(t)},M=e=>{if(null==e)throw new Error("Node cannot be null or undefined");return{dom:e}},P={fromHtml:(e,t)=>{const o=(t||document).createElement("div");if(o.innerHTML=e,!o.hasChildNodes()||o.childNodes.length>1){const t="HTML does not have a single root node";throw console.error(t,e),new Error(t)}return M(o.childNodes[0])},fromTag:(e,t)=>{const o=(t||document).createElement(e);return M(o)},fromText:(e,t)=>{const o=(t||document).createTextNode(e);return M(o)},fromDom:M,fromPoint:(e,t,o)=>u.from(e.dom.elementFromPoint(t,o)).map(M)},O=(e,t)=>{const o=e.dom;if(1!==o.nodeType)return!1;{const e=o;if(void 0!==e.matches)return e.matches(t);if(void 0!==e.msMatchesSelector)return e.msMatchesSelector(t);if(void 0!==e.webkitMatchesSelector)return e.webkitMatchesSelector(t);if(void 0!==e.mozMatchesSelector)return e.mozMatchesSelector(t);throw new Error("Browser lacks native selectors")}},k=e=>1!==e.nodeType&&9!==e.nodeType&&11!==e.nodeType||0===e.childElementCount,B=O,R=(L=/^\s+|\s+$/g,e=>e.replace(L,""));var L;const $=e=>void 0!==e.style&&i(e.style.getPropertyValue),I=e=>A(e)?e:P.fromDom(e.dom.ownerDocument),V=e=>u.from(e.dom.parentNode).map(P.fromDom),j=e=>u.from(e.dom.nextSibling).map(P.fromDom),q=e=>h(e.dom.childNodes,P.fromDom),F=i(Element.prototype.attachShadow)&&i(Node.prototype.getRootNode)?e=>P.fromDom(e.dom.getRootNode()):I,H=e=>P.fromDom(e.dom.host),z=e=>{const t=T(e)?e.dom.parentNode:e.dom;if(null==t||null===t.ownerDocument)return!1;const o=t.ownerDocument;return(e=>{const t=F(e);return C(o=t)&&a(o.dom.host)?u.some(t):u.none();var o})(P.fromDom(t)).fold((()=>o.body.contains(t)),(n=z,r=H,e=>n(r(e))));var n,r},K=(e,t)=>$(e)?e.style.getPropertyValue(t):"",U=(e,t)=>{V(e).each((o=>{o.dom.insertBefore(t.dom,e.dom)}))},Y=(e,t)=>{j(e).fold((()=>{V(e).each((e=>{_(e,t)}))}),(e=>{U(e,t)}))},_=(e,t)=>{e.dom.appendChild(t.dom)},G=(e,t)=>{f(t,((o,n)=>{const r=0===n?e:t[n-1];Y(r,o)}))},J=(e,t)=>{let o=[];return f(q(e),(e=>{t(e)&&(o=o.concat([e])),o=o.concat(J(e,t))})),o},Q=(e,t,o)=>{let n=e.dom;const r=i(o)?o:m;for(;n.parentNode;){n=n.parentNode;const e=P.fromDom(n);if(t(e))return u.some(e);if(r(e))break}return u.none()},W=e=>{const t=e.dom;null!==t.parentNode&&t.parentNode.removeChild(t)},X=(e,t,o)=>Q(e,(e=>O(e,t)),o),Z=(e,t)=>((e,t)=>{const o=void 0===t?document:t.dom;return k(o)?u.none():u.from(o.querySelector(e)).map(P.fromDom)})(t,e),ee=((e,t)=>{const o=t=>e(t)?u.from(t.dom.nodeValue):u.none();return{get:t=>{if(!e(t))throw new Error("Can only get text value of a text node");return o(t).getOr("")},getOption:o,set:(t,o)=>{if(!e(t))throw new Error("Can only set raw text value of a text node");t.dom.nodeValue=o}}})(T);var te=["body","p","div","article","aside","figcaption","figure","footer","header","nav","section","ol","ul","li","table","thead","tbody","tfoot","caption","tr","td","th","h1","h2","h3","h4","h5","h6","blockquote","pre","address"];const oe=(e,t)=>({element:e,offset:t}),ne=(e,t,o)=>e.property().isText(t)&&0===e.property().getText(t).trim().length||e.property().isComment(t)?o(t).bind((t=>ne(e,t,o).orThunk((()=>u.some(t))))):u.none(),re=(e,t)=>e.property().isText(t)?e.property().getText(t).length:e.property().children(t).length,se=(e,t)=>{const o=ne(e,t,e.query().prevSibling).getOr(t);if(e.property().isText(o))return oe(o,re(e,o));const n=e.property().children(o);return n.length>0?se(e,n[n.length-1]):oe(o,re(e,o))},ae=se,ie={up:l({selector:X,closest:(e,t,o)=>((e,t,o,n,r)=>((e,t)=>O(e,t))(o,n)?u.some(o):i(r)&&r(o)?u.none():t(o,n,r))(0,X,e,t,o),predicate:Q,all:(e,t)=>{const o=i(t)?t:m;let n=e.dom;const r=[];for(;null!==n.parentNode&&void 0!==n.parentNode;){const e=n.parentNode,t=P.fromDom(e);if(r.push(t),!0===o(t))break;n=e}return r}}),down:l({selector:(e,t)=>((e,t)=>{const o=void 0===t?document:t.dom;return k(o)?[]:h(o.querySelectorAll(e),P.fromDom)})(t,e),predicate:J}),styles:l({get:(e,t)=>{const o=e.dom,n=window.getComputedStyle(o).getPropertyValue(t);return""!==n||z(e)?n:K(o,t)},getRaw:(e,t)=>{const o=e.dom,n=K(o,t);return u.from(n).filter((e=>e.length>0))},set:(e,t,o)=>{((e,t,o)=>{if(!n(o))throw console.error("Invalid call to CSS.set. Property ",t,":: Value ",o,":: Element ",e),new Error("CSS value must be a string: "+o);$(e)&&e.style.setProperty(t,o)})(e.dom,t,o)},remove:(e,t)=>{((e,t)=>{$(e)&&e.style.removeProperty(t)})(e.dom,t),((e,t,o=c)=>e.exists((e=>o(e,t))))(E(e,"style").map(R),"")&&D(e,"style")}}),attrs:l({get:x,set:(e,t,o)=>{S(e.dom,t,o)},remove:D,copyTo:(e,t)=>{const o=(n=e.dom.attributes,r=(e,t)=>(e[t.name]=t.value,e),s={},f(n,((e,t)=>{s=r(s,e)})),s);var n,r,s;((e,t)=>{const o=e.dom;((e,t)=>{const o=y(e);for(let n=0,r=o.length;n{S(o,t,e)}))})(t,o)}}),insert:l({before:U,after:Y,afterAll:G,append:_,appendAll:(e,t)=>{f(t,(t=>{_(e,t)}))},prepend:(e,t)=>{(e=>((e,t)=>{const o=e.dom.childNodes;return u.from(o[0]).map(P.fromDom)})(e))(e).fold((()=>{_(e,t)}),(o=>{e.dom.insertBefore(t.dom,o.dom)}))},wrap:(e,t)=>{U(e,t),_(t,e)}}),remove:l({unwrap:e=>{const t=q(e);t.length>0&&G(e,t),W(e)},remove:W}),create:l({nu:P.fromTag,clone:e=>P.fromDom(e.dom.cloneNode(!1)),text:P.fromText}),query:l({comparePosition:(e,t)=>e.dom.compareDocumentPosition(t.dom),prevSibling:e=>u.from(e.dom.previousSibling).map(P.fromDom),nextSibling:j}),property:l({children:q,name:v,parent:V,document:e=>I(e).dom,isText:T,isComment:e=>8===w(e)||"#comment"===v(e),isElement:N,isSpecial:e=>{const t=v(e);return p(["script","noscript","iframe","noframes","noembed","title","style","textarea","xmp"],t)},getLanguage:e=>N(e)?E(e,"lang"):u.none(),getText:e=>ee.get(e),setText:(e,t)=>ee.set(e,t),isBoundary:e=>!!N(e)&&("body"===v(e)||p(te,v(e))),isEmptyTag:e=>!!N(e)&&p(["br","img","hr","input"],v(e)),isNonEditable:e=>N(e)&&"false"===x(e,"contenteditable")}),eq:(e,t)=>e.dom===t.dom,is:B},de="details",le="mce-accordion",ce="mce-accordion-summary",me="mce-accordion-body",ue="div";var ge=tinymce.util.Tools.resolve("tinymce.util.Tools");const pe=e=>"SUMMARY"===(null==e?void 0:e.nodeName),he=e=>"DETAILS"===(null==e?void 0:e.nodeName),fe=e=>e.hasAttribute("open"),ye=e=>{const t=e.selection.getNode();return pe(t)||Boolean(e.dom.getParent(t,pe))},ve=e=>!ye(e)&&e.dom.isEditable(e.selection.getNode()),we=e=>u.from(e.dom.getParent(e.selection.getNode(),he)),be=e=>(e.innerHTML='
',e),Ne=e=>be(e.dom.create("p")),Te=e=>t=>{((e,t)=>{if(pe(null==t?void 0:t.lastChild)){const o=Ne(e);t.appendChild(o),e.selection.setCursorLocation(o,0)}})(e,t),((e,t)=>{if(!pe(null==t?void 0:t.firstChild)){const o=(e=>be(e.dom.create("summary")))(e);t.prepend(o),e.selection.setCursorLocation(o,0)}})(e,t)},Ae=(e,t)=>{const o=null!=t?t:!fe(e);return o?e.setAttribute("open","open"):e.removeAttribute("open"),o},Ce=e=>{e.addCommand("InsertAccordion",(()=>(e=>{if(!ve(e))return;const o=P.fromDom(e.getBody()),n=(e=>{const o=(new Date).getTime(),n=Math.floor(1e9*Math.random());return t++,"acc_"+n+t+String(o)})(),r=e.dom.encode(e.selection.getRng().toString()||e.translate("Accordion summary...")),s=e.dom.encode(e.translate("Accordion body...")),a=`${r}`,i=`<${ue} class="${me}">

${s}

`;e.undoManager.transact((()=>{e.insertContent([`
`,a,i,"
"].join("")),Z(o,`[data-mce-id="${n}"]`).each((t=>{D(t,"data-mce-id"),Z(t,"summary").each((t=>{const o=e.dom.createRng(),n=ae(ie,t);o.setStart(n.element.dom,n.offset),o.setEnd(n.element.dom,n.offset),e.selection.setRng(o)}))}))}))})(e))),e.addCommand("ToggleAccordion",((t,o)=>((e,t)=>{we(e).each((o=>{((e,t,o)=>{e.dispatch("ToggledAccordion",{element:t,state:o})})(e,o,Ae(o,t))}))})(e,o))),e.addCommand("ToggleAllAccordions",((t,o)=>((e,t)=>{const o=Array.from(e.getBody().querySelectorAll("details"));0!==o.length&&(f(o,(e=>Ae(e,null!=t?t:!fe(e)))),((e,t,o)=>{e.dispatch("ToggledAllAccordions",{elements:t,state:o})})(e,o,t))})(e,o))),e.addCommand("RemoveAccordion",(()=>(e=>{we(e).each((t=>{const{nextSibling:o}=t;o?(e.selection.select(o,!0),e.selection.collapse(!0)):((e,t)=>{const o=Ne(e);t.insertAdjacentElement("afterend",o),e.selection.setCursorLocation(o,0)})(e,t),t.remove()}))})(e)))};var Se=tinymce.util.Tools.resolve("tinymce.html.Node");const xe=e=>{var t,o;return null!==(o=null===(t=e.attr("class"))||void 0===t?void 0:t.split(" "))&&void 0!==o?o:[]},Ee=(e,t)=>{const o=new Set([...xe(e),...t]),n=Array.from(o);n.length>0&&e.attr("class",n.join(" "))},De=(e,t)=>{const o=((e,o)=>{const n=[];for(let o=0,s=e.length;o0?o.join(" "):null)},Me=e=>e.name===de&&p(xe(e),le),Pe=e=>{const t=e.children();let o,n;const r=[];for(let e=0;e{const t=new Se("br",1);t.attr("data-mce-bogus","1"),e.empty(),e.append(t)};var ke=tinymce.util.Tools.resolve("tinymce.util.VK");const Be=e=>{(e=>{e.on("keydown",(t=>{(!t.shiftKey&&t.keyCode===ke.ENTER&&ye(e)||(e=>{const t=e.selection.getRng();return he(t.startContainer)&&t.collapsed&&0===t.startOffset})(e))&&(t.preventDefault(),e.execCommand("ToggleAccordion"))}))})(e),e.on("ExecCommand",(t=>{const o=t.command.toLowerCase();"delete"!==o&&"forwarddelete"!==o||!(e=>we(e).isSome())(e)||(e=>{ge.each(ge.grep(e.dom.select("details",e.getBody())),Te(e))})(e)}))};var Re=tinymce.util.Tools.resolve("tinymce.Env");const Le=e=>t=>{const o=()=>t.setEnabled(ve(e));return e.on("NodeChange",o),()=>e.off("NodeChange",o)};e.add("accordion",(e=>{(e=>{const t=()=>e.execCommand("InsertAccordion");e.ui.registry.addButton("accordion",{icon:"accordion",tooltip:"Insert accordion",onSetup:Le(e),onAction:t}),e.ui.registry.addMenuItem("accordion",{icon:"accordion",text:"Accordion",onSetup:Le(e),onAction:t}),e.ui.registry.addToggleButton("accordiontoggle",{icon:"accordion-toggle",tooltip:"Toggle accordion",onAction:()=>e.execCommand("ToggleAccordion")}),e.ui.registry.addToggleButton("accordionremove",{icon:"remove",tooltip:"Delete accordion",onAction:()=>e.execCommand("RemoveAccordion")}),e.ui.registry.addContextToolbar("accordion",{predicate:t=>e.dom.is(t,"details")&&e.getBody().contains(t)&&e.dom.isEditable(t.parentNode),items:"accordiontoggle accordionremove",scope:"node",position:"node"})})(e),Ce(e),Be(e),(e=>{e.on("PreInit",(()=>{const{serializer:t,parser:o}=e;o.addNodeFilter(de,(e=>{for(let t=0;t0)for(let e=0;e{const t=new Set([ce]);for(let o=0;o{Re.browser.isSafari()&&e.on("click",(t=>{if(pe(t.target)){const o=t.target,n=e.selection.getRng();n.collapsed&&n.startContainer===o.parentNode&&0===n.startOffset&&e.selection.setCursorLocation(o,0)}}))})(e)}))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/advlist/plugin.min.js b/apps/web-antd/public/tinymce/plugins/advlist/plugin.min.js new file mode 100644 index 0000000..dac3959 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/advlist/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";var t=tinymce.util.Tools.resolve("tinymce.PluginManager");const e=(t,e,s)=>{const r="UL"===e?"InsertUnorderedList":"InsertOrderedList";t.execCommand(r,!1,!1===s?null:{"list-style-type":s})},s=t=>e=>e.options.get(t),r=s("advlist_number_styles"),n=s("advlist_bullet_styles"),i=t=>null==t,l=t=>!i(t);var o=tinymce.util.Tools.resolve("tinymce.util.Tools");class a{constructor(t,e){this.tag=t,this.value=e}static some(t){return new a(!0,t)}static none(){return a.singletonNone}fold(t,e){return this.tag?e(this.value):t()}isSome(){return this.tag}isNone(){return!this.tag}map(t){return this.tag?a.some(t(this.value)):a.none()}bind(t){return this.tag?t(this.value):a.none()}exists(t){return this.tag&&t(this.value)}forall(t){return!this.tag||t(this.value)}filter(t){return!this.tag||t(this.value)?this:a.none()}getOr(t){return this.tag?this.value:t}or(t){return this.tag?this:t}getOrThunk(t){return this.tag?this.value:t()}orThunk(t){return this.tag?this:t()}getOrDie(t){if(this.tag)return this.value;throw new Error(null!=t?t:"Called getOrDie on None")}static from(t){return l(t)?a.some(t):a.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(t){this.tag&&t(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}a.singletonNone=new a(!1);const u=t=>e=>l(e)&&t.test(e.nodeName),d=u(/^(OL|UL|DL)$/),g=u(/^(TH|TD)$/),c=t=>i(t)||"default"===t?"":t,h=(t,e)=>s=>((t,e)=>{const s=t.selection.getNode();return e({parents:t.dom.getParents(s),element:s}),t.on("NodeChange",e),()=>t.off("NodeChange",e)})(t,(r=>((t,r)=>{const n=t.selection.getStart(!0);s.setActive(((t,e,s)=>((t,e,s)=>{for(let e=0,n=t.length;ee.nodeName===s&&((t,e)=>t.dom.isChildOf(e,t.getBody()))(t,e))))(t,r,e)),s.setEnabled(!((t,e)=>{const s=t.dom.getParent(e,"ol,ul,dl");return((t,e)=>null!==e&&!t.dom.isEditable(e))(t,s)&&t.selection.isEditable()})(t,n)&&t.selection.isEditable())})(t,r.parents))),m=(t,s,r,n,i,l)=>{l.length>1?((t,s,r,n,i,l)=>{t.ui.registry.addSplitButton(s,{tooltip:r,icon:"OL"===i?"ordered-list":"unordered-list",presets:"listpreview",columns:3,fetch:t=>{t(o.map(l,(t=>{const e="OL"===i?"num":"bull",s="disc"===t||"decimal"===t?"default":t,r=c(t),n=(t=>t.replace(/\-/g," ").replace(/\b\w/g,(t=>t.toUpperCase())))(t);return{type:"choiceitem",value:r,icon:"list-"+e+"-"+s,text:n}})))},onAction:()=>t.execCommand(n),onItemAction:(s,r)=>{e(t,i,r)},select:e=>{const s=(t=>{const e=t.dom.getParent(t.selection.getNode(),"ol,ul"),s=t.dom.getStyle(e,"listStyleType");return a.from(s)})(t);return s.map((t=>e===t)).getOr(!1)},onSetup:h(t,i)})})(t,s,r,n,i,l):((t,s,r,n,i,l)=>{t.ui.registry.addToggleButton(s,{active:!1,tooltip:r,icon:"OL"===i?"ordered-list":"unordered-list",onSetup:h(t,i),onAction:()=>t.queryCommandState(n)||""===l?t.execCommand(n):e(t,i,l)})})(t,s,r,n,i,c(l[0]))};t.add("advlist",(t=>{t.hasPlugin("lists")?((t=>{const e=t.options.register;e("advlist_number_styles",{processor:"string[]",default:"default,lower-alpha,lower-greek,lower-roman,upper-alpha,upper-roman".split(",")}),e("advlist_bullet_styles",{processor:"string[]",default:"default,circle,square".split(",")})})(t),(t=>{m(t,"numlist","Numbered list","InsertOrderedList","OL",r(t)),m(t,"bullist","Bullet list","InsertUnorderedList","UL",n(t))})(t),(t=>{t.addCommand("ApplyUnorderedListStyle",((s,r)=>{e(t,"UL",r["list-style-type"])})),t.addCommand("ApplyOrderedListStyle",((s,r)=>{e(t,"OL",r["list-style-type"])}))})(t)):console.error("Please use the Lists plugin together with the List Styles plugin.")}))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/anchor/plugin.min.js b/apps/web-antd/public/tinymce/plugins/anchor/plugin.min.js new file mode 100644 index 0000000..59c4d25 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/anchor/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),t=tinymce.util.Tools.resolve("tinymce.dom.RangeUtils"),o=tinymce.util.Tools.resolve("tinymce.util.Tools");const n=("allow_html_in_named_anchor",e=>e.options.get("allow_html_in_named_anchor"));const a="a:not([href])",r=e=>!e,i=e=>e.getAttribute("id")||e.getAttribute("name")||"",l=e=>(e=>"a"===e.nodeName.toLowerCase())(e)&&!e.getAttribute("href")&&""!==i(e),s=e=>e.dom.getParent(e.selection.getStart(),a),d=(e,a)=>{const r=s(e);r?((e,t,o)=>{o.removeAttribute("name"),o.id=t,e.addVisual(),e.undoManager.add()})(e,a,r):((e,a)=>{e.undoManager.transact((()=>{n(e)||e.selection.collapse(!0),e.selection.isCollapsed()?e.insertContent(e.dom.createHTML("a",{id:a})):((e=>{const n=e.dom;t(n).walk(e.selection.getRng(),(e=>{o.each(e,(e=>{var t;l(t=e)&&!t.firstChild&&n.remove(e,!1)}))}))})(e),e.formatter.remove("namedAnchor",void 0,void 0,!0),e.formatter.apply("namedAnchor",{value:a}),e.addVisual())}))})(e,a),e.focus()},c=e=>(e=>r(e.attr("href"))&&!r(e.attr("id")||e.attr("name")))(e)&&!e.firstChild,m=e=>t=>{for(let o=0;ot=>{const o=()=>{t.setEnabled(e.selection.isEditable())};return e.on("NodeChange",o),o(),()=>{e.off("NodeChange",o)}};e.add("anchor",(e=>{(e=>{(0,e.options.register)("allow_html_in_named_anchor",{processor:"boolean",default:!1})})(e),(e=>{e.on("PreInit",(()=>{e.parser.addNodeFilter("a",m("false")),e.serializer.addNodeFilter("a",m(null))}))})(e),(e=>{e.addCommand("mceAnchor",(()=>{(e=>{const t=(e=>{const t=s(e);return t?i(t):""})(e);e.windowManager.open({title:"Anchor",size:"normal",body:{type:"panel",items:[{name:"id",type:"input",label:"ID",placeholder:"example"}]},buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],initialData:{id:t},onSubmit:t=>{((e,t)=>/^[A-Za-z][A-Za-z0-9\-:._]*$/.test(t)?(d(e,t),!0):(e.windowManager.alert("ID should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores."),!1))(e,t.getData().id)&&t.close()}})})(e)}))})(e),(e=>{const t=()=>e.execCommand("mceAnchor");e.ui.registry.addToggleButton("anchor",{icon:"bookmark",tooltip:"Anchor",onAction:t,onSetup:t=>{const o=e.selection.selectorChangedWithUnbind("a:not([href])",t.setActive).unbind,n=u(e)(t);return()=>{o(),n()}}}),e.ui.registry.addMenuItem("anchor",{icon:"bookmark",text:"Anchor...",onAction:t,onSetup:u(e)})})(e),e.on("PreInit",(()=>{(e=>{e.formatter.register("namedAnchor",{inline:"a",selector:a,remove:"all",split:!0,deep:!0,attributes:{id:"%value"},onmatch:(e,t,o)=>l(e)})})(e)}))}))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/autolink/plugin.min.js b/apps/web-antd/public/tinymce/plugins/autolink/plugin.min.js new file mode 100644 index 0000000..ba5d144 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/autolink/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>t=>t.options.get(e),n=t("autolink_pattern"),o=t("link_default_target"),r=t("link_default_protocol"),a=t("allow_unsafe_link_target"),s=("string",e=>"string"===(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(n=o=e,(r=String).prototype.isPrototypeOf(n)||(null===(a=o.constructor)||void 0===a?void 0:a.name)===r.name)?"string":t;var n,o,r,a})(e));const l=(void 0,e=>undefined===e);const i=e=>!(e=>null==e)(e),c=Object.hasOwnProperty,d=e=>"\ufeff"===e;var u=tinymce.util.Tools.resolve("tinymce.dom.TextSeeker");const f=e=>/^[(\[{ \u00a0]$/.test(e),g=(e,t,n)=>{for(let o=t-1;o>=0;o--){const t=e.charAt(o);if(!d(t)&&n(t))return o}return-1},m=(e,t)=>{var o;const a=e.schema.getVoidElements(),s=n(e),{dom:i,selection:d}=e;if(null!==i.getParent(d.getNode(),"a[href]"))return null;const m=d.getRng(),k=u(i,(e=>{return i.isBlock(e)||(t=a,n=e.nodeName.toLowerCase(),c.call(t,n))||"false"===i.getContentEditable(e);var t,n})),{container:p,offset:y}=((e,t)=>{let n=e,o=t;for(;1===n.nodeType&&n.childNodes[o];)n=n.childNodes[o],o=3===n.nodeType?n.data.length:n.childNodes.length;return{container:n,offset:o}})(m.endContainer,m.endOffset),w=null!==(o=i.getParent(p,i.isBlock))&&void 0!==o?o:i.getRoot(),h=k.backwards(p,y+t,((e,t)=>{const n=e.data,o=g(n,t,(r=f,e=>!r(e)));var r,a;return-1===o||(a=n[o],/[?!,.;:]/.test(a))?o:o+1}),w);if(!h)return null;let v=h.container;const _=k.backwards(h.container,h.offset,((e,t)=>{v=e;const n=g(e.data,t,f);return-1===n?n:n+1}),w),A=i.createRng();_?A.setStart(_.container,_.offset):A.setStart(v,0),A.setEnd(h.container,h.offset);const C=A.toString().replace(/\uFEFF/g,"").match(s);if(C){let t=C[0];return $="www.",(b=t).length>=4&&b.substr(0,4)===$?t=r(e)+"://"+t:((e,t,n=0,o)=>{const r=e.indexOf(t,n);return-1!==r&&(!!l(o)||r+t.length<=o)})(t,"@")&&!(e=>/^([A-Za-z][A-Za-z\d.+-]*:\/\/)|mailto:/.test(e))(t)&&(t="mailto:"+t),{rng:A,url:t}}var b,$;return null},k=(e,t)=>{const{dom:n,selection:r}=e,{rng:l,url:i}=t,c=r.getBookmark();r.setRng(l);const d="createlink",u={command:d,ui:!1,value:i};if(!e.dispatch("BeforeExecCommand",u).isDefaultPrevented()){e.getDoc().execCommand(d,!1,i),e.dispatch("ExecCommand",u);const t=o(e);if(s(t)){const o=r.getNode();n.setAttrib(o,"target",t),"_blank"!==t||a(e)||n.setAttrib(o,"rel","noopener")}}r.moveToBookmark(c),e.nodeChanged()},p=e=>{const t=m(e,-1);i(t)&&k(e,t)},y=p;e.add("autolink",(e=>{(e=>{const t=e.options.register;t("autolink_pattern",{processor:"regexp",default:new RegExp("^"+/(?:[A-Za-z][A-Za-z\d.+-]{0,14}:\/\/(?:[-.~*+=!&;:'%@?^${}(),\w]+@)?|www\.|[-;:&=+$,.\w]+@)[A-Za-z\d-]+(?:\.[A-Za-z\d-]+)*(?::\d+)?(?:\/(?:[-.~*+=!;:'%@$(),\/\w]*[-~*+=%@$()\/\w])?)?(?:\?(?:[-.~*+=!&;:'%@?^${}(),\/\w]+))?(?:#(?:[-.~*+=!&;:'%@?^${}(),\/\w]+))?/g.source+"$","i")}),t("link_default_target",{processor:"string"}),t("link_default_protocol",{processor:"string",default:"https"})})(e),(e=>{e.on("keydown",(t=>{13!==t.keyCode||t.isDefaultPrevented()||(e=>{const t=m(e,0);i(t)&&k(e,t)})(e)})),e.on("keyup",(t=>{32===t.keyCode?p(e):(48===t.keyCode&&t.shiftKey||221===t.keyCode)&&y(e)}))})(e)}))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/autoresize/plugin.min.js b/apps/web-antd/public/tinymce/plugins/autoresize/plugin.min.js new file mode 100644 index 0000000..b817813 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/autoresize/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),t=tinymce.util.Tools.resolve("tinymce.Env");const o=e=>t=>t.options.get(e),n=o("min_height"),s=o("max_height"),i=o("autoresize_overflow_padding"),r=o("autoresize_bottom_margin"),g=(e,t)=>{const o=e.getBody();o&&(o.style.overflowY=t?"":"hidden",t||(o.scrollTop=0))},l=(e,t,o,n)=>{var s;const i=parseInt(null!==(s=e.getStyle(t,o,n))&&void 0!==s?s:"",10);return isNaN(i)?0:i},a=(e,o,r,c)=>{var d;const u=e.dom,h=e.getDoc();if(!h)return;if((e=>e.plugins.fullscreen&&e.plugins.fullscreen.isFullscreen())(e))return void g(e,!0);const m=h.documentElement,f=c?c():i(e),p=null!==(d=n(e))&&void 0!==d?d:e.getElement().offsetHeight;let y=p;const S=l(u,m,"margin-top",!0),v=l(u,m,"margin-bottom",!0);let C=m.offsetHeight+S+v+f;C<0&&(C=0);const H=e.getContainer().offsetHeight-e.getContentAreaContainer().offsetHeight;C+H>p&&(y=C+H);const b=s(e);b&&y>b?(y=b,g(e,!0)):g(e,!1);const w=o.get();if(w.set&&(e.dom.setStyles(e.getDoc().documentElement,{"min-height":0}),e.dom.setStyles(e.getBody(),{"min-height":"inherit"})),y!==w.totalHeight&&(C-f!==w.contentHeight||!w.set)){const n=y-w.totalHeight;if(u.setStyle(e.getContainer(),"height",y+"px"),o.set({totalHeight:y,contentHeight:C,set:!0}),(e=>{e.dispatch("ResizeEditor")})(e),t.browser.isSafari()&&(t.os.isMacOS()||t.os.isiOS())){const t=e.getWin();t.scrollTo(t.pageXOffset,t.pageYOffset)}e.hasFocus()&&(e=>{if("setcontent"===(null==e?void 0:e.type.toLowerCase())){const t=e;return!0===t.selection||!0===t.paste}return!1})(r)&&e.selection.scrollIntoView(),(t.browser.isSafari()||t.browser.isChromium())&&n<0&&a(e,o,r,c)}};e.add("autoresize",(e=>{if((e=>{const t=e.options.register;t("autoresize_overflow_padding",{processor:"number",default:1}),t("autoresize_bottom_margin",{processor:"number",default:50})})(e),e.options.isSet("resize")||e.options.set("resize",!1),!e.inline){const o=(e=>{let t={totalHeight:0,contentHeight:0,set:!1};return{get:()=>t,set:e=>{t=e}}})();((e,t)=>{e.addCommand("mceAutoResize",(()=>{a(e,t)}))})(e,o),((e,o)=>{const n=()=>r(e);e.on("init",(s=>{const r=i(e),g=e.dom;g.setStyles(e.getDoc().documentElement,{height:"auto"}),t.browser.isEdge()||t.browser.isIE()?g.setStyles(e.getBody(),{paddingLeft:r,paddingRight:r,"min-height":0}):g.setStyles(e.getBody(),{paddingLeft:r,paddingRight:r}),a(e,o,s,n)})),e.on("NodeChange SetContent keyup FullscreenStateChanged ResizeContent",(t=>{a(e,o,t,n)}))})(e,o)}}))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/autosave/plugin.min.js b/apps/web-antd/public/tinymce/plugins/autosave/plugin.min.js new file mode 100644 index 0000000..944621b --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/autosave/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";var t=tinymce.util.Tools.resolve("tinymce.PluginManager");const e=("string",t=>"string"===(t=>{const e=typeof t;return null===t?"null":"object"===e&&Array.isArray(t)?"array":"object"===e&&(r=o=t,(a=String).prototype.isPrototypeOf(r)||(null===(s=o.constructor)||void 0===s?void 0:s.name)===a.name)?"string":e;var r,o,a,s})(t));const r=(void 0,t=>undefined===t);var o=tinymce.util.Tools.resolve("tinymce.util.Delay"),a=tinymce.util.Tools.resolve("tinymce.util.LocalStorage"),s=tinymce.util.Tools.resolve("tinymce.util.Tools");const n=t=>{const e=/^(\d+)([ms]?)$/.exec(t);return(e&&e[2]?{s:1e3,m:6e4}[e[2]]:1)*parseInt(t,10)},i=t=>e=>e.options.get(t),u=i("autosave_ask_before_unload"),l=i("autosave_restore_when_empty"),c=i("autosave_interval"),d=i("autosave_retention"),m=t=>{const e=document.location;return t.options.get("autosave_prefix").replace(/{path}/g,e.pathname).replace(/{query}/g,e.search).replace(/{hash}/g,e.hash).replace(/{id}/g,t.id)},v=(t,e)=>{if(r(e))return t.dom.isEmpty(t.getBody());{const r=s.trim(e);if(""===r)return!0;{const e=(new DOMParser).parseFromString(r,"text/html");return t.dom.isEmpty(e)}}},f=t=>{var e;const r=parseInt(null!==(e=a.getItem(m(t)+"time"))&&void 0!==e?e:"0",10)||0;return!((new Date).getTime()-r>d(t)&&(p(t,!1),1))},p=(t,e)=>{const r=m(t);a.removeItem(r+"draft"),a.removeItem(r+"time"),!1!==e&&(t=>{t.dispatch("RemoveDraft")})(t)},g=t=>{const e=m(t);!v(t)&&t.isDirty()&&(a.setItem(e+"draft",t.getContent({format:"raw",no_events:!0})),a.setItem(e+"time",(new Date).getTime().toString()),(t=>{t.dispatch("StoreDraft")})(t))},y=t=>{var e;const r=m(t);f(t)&&(t.setContent(null!==(e=a.getItem(r+"draft"))&&void 0!==e?e:"",{format:"raw"}),(t=>{t.dispatch("RestoreDraft")})(t))};var D=tinymce.util.Tools.resolve("tinymce.EditorManager");const h=t=>e=>{e.setEnabled(f(t));const r=()=>e.setEnabled(f(t));return t.on("StoreDraft RestoreDraft RemoveDraft",r),()=>t.off("StoreDraft RestoreDraft RemoveDraft",r)};t.add("autosave",(t=>((t=>{const r=t.options.register,o=t=>{const r=e(t);return r?{value:n(t),valid:r}:{valid:!1,message:"Must be a string."}};r("autosave_ask_before_unload",{processor:"boolean",default:!0}),r("autosave_prefix",{processor:"string",default:"tinymce-autosave-{path}{query}{hash}-{id}-"}),r("autosave_restore_when_empty",{processor:"boolean",default:!1}),r("autosave_interval",{processor:o,default:"30s"}),r("autosave_retention",{processor:o,default:"20m"})})(t),(t=>{t.editorManager.on("BeforeUnload",(t=>{let e;s.each(D.get(),(t=>{t.plugins.autosave&&t.plugins.autosave.storeDraft(),!e&&t.isDirty()&&u(t)&&(e=t.translate("You have unsaved changes are you sure you want to navigate away?"))})),e&&(t.preventDefault(),t.returnValue=e)}))})(t),(t=>{(t=>{const e=c(t);o.setEditorInterval(t,(()=>{g(t)}),e)})(t);const e=()=>{(t=>{t.undoManager.transact((()=>{y(t),p(t)})),t.focus()})(t)};t.ui.registry.addButton("restoredraft",{tooltip:"Restore last draft",icon:"restore-draft",onAction:e,onSetup:h(t)}),t.ui.registry.addMenuItem("restoredraft",{text:"Restore last draft",icon:"restore-draft",onAction:e,onSetup:h(t)})})(t),t.on("init",(()=>{l(t)&&t.dom.isEmpty(t.getBody())&&y(t)})),(t=>({hasDraft:()=>f(t),storeDraft:()=>g(t),restoreDraft:()=>y(t),removeDraft:e=>p(t,e),isEmpty:e=>v(t,e)}))(t))))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/charmap/plugin.min.js b/apps/web-antd/public/tinymce/plugins/charmap/plugin.min.js new file mode 100644 index 0000000..f86de54 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/charmap/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=(e,t)=>{const r=((e,t)=>e.dispatch("insertCustomChar",{chr:t}))(e,t).chr;e.execCommand("mceInsertContent",!1,r)},r=e=>t=>e===t,a=("array",e=>"array"===(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(r=a=e,(n=String).prototype.isPrototypeOf(r)||(null===(i=a.constructor)||void 0===i?void 0:i.name)===n.name)?"string":t;var r,a,n,i})(e));const n=r(null),i=r(void 0),o=e=>"function"==typeof e,s=(!1,()=>false);class l{constructor(e,t){this.tag=e,this.value=t}static some(e){return new l(!0,e)}static none(){return l.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?l.some(e(this.value)):l.none()}bind(e){return this.tag?e(this.value):l.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:l.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return null==e?l.none():l.some(e)}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}l.singletonNone=new l(!1);const c=Array.prototype.push,u=(e,t)=>{const r=e.length,a=new Array(r);for(let n=0;nt=>t.options.get(e),m=h("charmap"),p=h("charmap_append"),d=g.isArray,f="User Defined",y=e=>{return d(e)?(t=e,g.grep(t,(e=>d(e)&&2===e.length))):"function"==typeof e?e():[];var t},b=e=>{const t=((e,t)=>{const r=m(e);r&&(t=[{name:f,characters:y(r)}]);const a=p(e);if(a){const e=g.grep(t,(e=>e.name===f));return e.length?(e[0].characters=[...e[0].characters,...y(a)],t):t.concat({name:f,characters:y(a)})}return t})(e,[{name:"Currency",characters:[[36,"dollar sign"],[162,"cent sign"],[8364,"euro sign"],[163,"pound sign"],[165,"yen sign"],[164,"currency sign"],[8352,"euro-currency sign"],[8353,"colon sign"],[8354,"cruzeiro sign"],[8355,"french franc sign"],[8356,"lira sign"],[8357,"mill sign"],[8358,"naira sign"],[8359,"peseta sign"],[8360,"rupee sign"],[8361,"won sign"],[8362,"new sheqel sign"],[8363,"dong sign"],[8365,"kip sign"],[8366,"tugrik sign"],[8367,"drachma sign"],[8368,"german penny symbol"],[8369,"peso sign"],[8370,"guarani sign"],[8371,"austral sign"],[8372,"hryvnia sign"],[8373,"cedi sign"],[8374,"livre tournois sign"],[8375,"spesmilo sign"],[8376,"tenge sign"],[8377,"indian rupee sign"],[8378,"turkish lira sign"],[8379,"nordic mark sign"],[8380,"manat sign"],[8381,"ruble sign"],[20870,"yen character"],[20803,"yuan character"],[22291,"yuan character, in hong kong and taiwan"],[22278,"yen/yuan character variant one"]]},{name:"Text",characters:[[169,"copyright sign"],[174,"registered sign"],[8482,"trade mark sign"],[8240,"per mille sign"],[181,"micro sign"],[183,"middle dot"],[8226,"bullet"],[8230,"three dot leader"],[8242,"minutes / feet"],[8243,"seconds / inches"],[167,"section sign"],[182,"paragraph sign"],[223,"sharp s / ess-zed"]]},{name:"Quotations",characters:[[8249,"single left-pointing angle quotation mark"],[8250,"single right-pointing angle quotation mark"],[171,"left pointing guillemet"],[187,"right pointing guillemet"],[8216,"left single quotation mark"],[8217,"right single quotation mark"],[8220,"left double quotation mark"],[8221,"right double quotation mark"],[8218,"single low-9 quotation mark"],[8222,"double low-9 quotation mark"],[60,"less-than sign"],[62,"greater-than sign"],[8804,"less-than or equal to"],[8805,"greater-than or equal to"],[8211,"en dash"],[8212,"em dash"],[175,"macron"],[8254,"overline"],[164,"currency sign"],[166,"broken bar"],[168,"diaeresis"],[161,"inverted exclamation mark"],[191,"turned question mark"],[710,"circumflex accent"],[732,"small tilde"],[176,"degree sign"],[8722,"minus sign"],[177,"plus-minus sign"],[247,"division sign"],[8260,"fraction slash"],[215,"multiplication sign"],[185,"superscript one"],[178,"superscript two"],[179,"superscript three"],[188,"fraction one quarter"],[189,"fraction one half"],[190,"fraction three quarters"]]},{name:"Mathematical",characters:[[402,"function / florin"],[8747,"integral"],[8721,"n-ary sumation"],[8734,"infinity"],[8730,"square root"],[8764,"similar to"],[8773,"approximately equal to"],[8776,"almost equal to"],[8800,"not equal to"],[8801,"identical to"],[8712,"element of"],[8713,"not an element of"],[8715,"contains as member"],[8719,"n-ary product"],[8743,"logical and"],[8744,"logical or"],[172,"not sign"],[8745,"intersection"],[8746,"union"],[8706,"partial differential"],[8704,"for all"],[8707,"there exists"],[8709,"diameter"],[8711,"backward difference"],[8727,"asterisk operator"],[8733,"proportional to"],[8736,"angle"]]},{name:"Extended Latin",characters:[[192,"A - grave"],[193,"A - acute"],[194,"A - circumflex"],[195,"A - tilde"],[196,"A - diaeresis"],[197,"A - ring above"],[256,"A - macron"],[198,"ligature AE"],[199,"C - cedilla"],[200,"E - grave"],[201,"E - acute"],[202,"E - circumflex"],[203,"E - diaeresis"],[274,"E - macron"],[204,"I - grave"],[205,"I - acute"],[206,"I - circumflex"],[207,"I - diaeresis"],[298,"I - macron"],[208,"ETH"],[209,"N - tilde"],[210,"O - grave"],[211,"O - acute"],[212,"O - circumflex"],[213,"O - tilde"],[214,"O - diaeresis"],[216,"O - slash"],[332,"O - macron"],[338,"ligature OE"],[352,"S - caron"],[217,"U - grave"],[218,"U - acute"],[219,"U - circumflex"],[220,"U - diaeresis"],[362,"U - macron"],[221,"Y - acute"],[376,"Y - diaeresis"],[562,"Y - macron"],[222,"THORN"],[224,"a - grave"],[225,"a - acute"],[226,"a - circumflex"],[227,"a - tilde"],[228,"a - diaeresis"],[229,"a - ring above"],[257,"a - macron"],[230,"ligature ae"],[231,"c - cedilla"],[232,"e - grave"],[233,"e - acute"],[234,"e - circumflex"],[235,"e - diaeresis"],[275,"e - macron"],[236,"i - grave"],[237,"i - acute"],[238,"i - circumflex"],[239,"i - diaeresis"],[299,"i - macron"],[240,"eth"],[241,"n - tilde"],[242,"o - grave"],[243,"o - acute"],[244,"o - circumflex"],[245,"o - tilde"],[246,"o - diaeresis"],[248,"o slash"],[333,"o macron"],[339,"ligature oe"],[353,"s - caron"],[249,"u - grave"],[250,"u - acute"],[251,"u - circumflex"],[252,"u - diaeresis"],[363,"u - macron"],[253,"y - acute"],[254,"thorn"],[255,"y - diaeresis"],[563,"y - macron"],[913,"Alpha"],[914,"Beta"],[915,"Gamma"],[916,"Delta"],[917,"Epsilon"],[918,"Zeta"],[919,"Eta"],[920,"Theta"],[921,"Iota"],[922,"Kappa"],[923,"Lambda"],[924,"Mu"],[925,"Nu"],[926,"Xi"],[927,"Omicron"],[928,"Pi"],[929,"Rho"],[931,"Sigma"],[932,"Tau"],[933,"Upsilon"],[934,"Phi"],[935,"Chi"],[936,"Psi"],[937,"Omega"],[945,"alpha"],[946,"beta"],[947,"gamma"],[948,"delta"],[949,"epsilon"],[950,"zeta"],[951,"eta"],[952,"theta"],[953,"iota"],[954,"kappa"],[955,"lambda"],[956,"mu"],[957,"nu"],[958,"xi"],[959,"omicron"],[960,"pi"],[961,"rho"],[962,"final sigma"],[963,"sigma"],[964,"tau"],[965,"upsilon"],[966,"phi"],[967,"chi"],[968,"psi"],[969,"omega"]]},{name:"Symbols",characters:[[8501,"alef symbol"],[982,"pi symbol"],[8476,"real part symbol"],[978,"upsilon - hook symbol"],[8472,"Weierstrass p"],[8465,"imaginary part"]]},{name:"Arrows",characters:[[8592,"leftwards arrow"],[8593,"upwards arrow"],[8594,"rightwards arrow"],[8595,"downwards arrow"],[8596,"left right arrow"],[8629,"carriage return"],[8656,"leftwards double arrow"],[8657,"upwards double arrow"],[8658,"rightwards double arrow"],[8659,"downwards double arrow"],[8660,"left right double arrow"],[8756,"therefore"],[8834,"subset of"],[8835,"superset of"],[8836,"not a subset of"],[8838,"subset of or equal to"],[8839,"superset of or equal to"],[8853,"circled plus"],[8855,"circled times"],[8869,"perpendicular"],[8901,"dot operator"],[8968,"left ceiling"],[8969,"right ceiling"],[8970,"left floor"],[8971,"right floor"],[9001,"left-pointing angle bracket"],[9002,"right-pointing angle bracket"],[9674,"lozenge"],[9824,"black spade suit"],[9827,"black club suit"],[9829,"black heart suit"],[9830,"black diamond suit"],[8194,"en space"],[8195,"em space"],[8201,"thin space"],[8204,"zero width non-joiner"],[8205,"zero width joiner"],[8206,"left-to-right mark"],[8207,"right-to-left mark"]]}]);return t.length>1?[{name:"All",characters:(r=t,n=e=>e.characters,(e=>{const t=[];for(let r=0,n=e.length;r{let t=e;return{get:()=>t,set:e=>{t=e}}},v=(e,t,r=0,a)=>{const n=e.indexOf(t,r);return-1!==n&&(!!i(a)||n+t.length<=a)},k=String.fromCodePoint,C=(e,t)=>{const r=[],a=t.toLowerCase();return((e,t)=>{for(let t=0,i=e.length;t!!v(k(e).toLowerCase(),r)||v(t.toLowerCase(),r)||v(t.toLowerCase().replace(/\s+/g,""),r))((n=e[t])[0],n[1],a)&&r.push(n);var n})(e.characters),u(r,(e=>({text:e[1],value:k(e[0]),icon:k(e[0])})))},x="pattern",A=(e,r)=>{const a=()=>[{label:"Search",type:"input",name:x},{type:"collection",name:"results"}],i=1===r.length?w(f):w("All"),o=((e,t)=>{let r=null;const a=()=>{n(r)||(clearTimeout(r),r=null)};return{cancel:a,throttle:(...t)=>{a(),r=setTimeout((()=>{r=null,e.apply(null,t)}),40)}}})((e=>{const t=e.getData().pattern;((e,t)=>{var a,n;(a=r,n=e=>e.name===i.get(),((e,t,r)=>{for(let a=0,n=e.length;a{const a=C(r,t);e.setData({results:a})}))})(e,t)})),c={title:"Special Character",size:"normal",body:1===r.length?{type:"panel",items:a()}:{type:"tabpanel",tabs:u(r,(e=>({title:e.name,name:e.name,items:a()})))},buttons:[{type:"cancel",name:"close",text:"Close",primary:!0}],initialData:{pattern:"",results:C(r[0],"")},onAction:(r,a)=>{"results"===a.name&&(t(e,a.value),r.close())},onTabChange:(e,t)=>{i.set(t.newTabName),o.throttle(e)},onChange:(e,t)=>{t.name===x&&o.throttle(e)}};e.windowManager.open(c).focus(x)},q=e=>t=>{const r=()=>{t.setEnabled(e.selection.isEditable())};return e.on("NodeChange",r),r(),()=>{e.off("NodeChange",r)}};e.add("charmap",(e=>{(e=>{const t=e.options.register,r=e=>o(e)||a(e);t("charmap",{processor:r}),t("charmap_append",{processor:r})})(e);const r=b(e);return((e,t)=>{e.addCommand("mceShowCharmap",(()=>{A(e,t)}))})(e,r),(e=>{const t=()=>e.execCommand("mceShowCharmap");e.ui.registry.addButton("charmap",{icon:"insert-character",tooltip:"Special character",onAction:t,onSetup:q(e)}),e.ui.registry.addMenuItem("charmap",{icon:"insert-character",text:"Special character...",onAction:t,onSetup:q(e)})})(e),((e,t)=>{e.ui.registry.addAutocompleter("charmap",{trigger:":",columns:"auto",minChars:2,fetch:(e,r)=>new Promise(((r,a)=>{r(C(t,e))})),onAction:(t,r,a)=>{e.selection.setRng(r),e.insertContent(a),t.hide()}})})(e,r[0]),(e=>({getCharMap:()=>b(e),insertChar:r=>{t(e,r)}}))(e)}))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/code/plugin.min.js b/apps/web-antd/public/tinymce/plugins/code/plugin.min.js new file mode 100644 index 0000000..4d91653 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/code/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";tinymce.util.Tools.resolve("tinymce.PluginManager").add("code",(e=>((e=>{e.addCommand("mceCodeEditor",(()=>{(e=>{const o=(e=>e.getContent({source_view:!0}))(e);e.windowManager.open({title:"Source Code",size:"large",body:{type:"panel",items:[{type:"textarea",name:"code"}]},buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],initialData:{code:o},onSubmit:o=>{((e,o)=>{e.focus(),e.undoManager.transact((()=>{e.setContent(o)})),e.selection.setCursorLocation(),e.nodeChanged()})(e,o.getData().code),o.close()}})})(e)}))})(e),(e=>{const o=()=>e.execCommand("mceCodeEditor");e.ui.registry.addButton("code",{icon:"sourcecode",tooltip:"Source code",onAction:o}),e.ui.registry.addMenuItem("code",{icon:"sourcecode",text:"Source code",onAction:o})})(e),{})))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/codesample/plugin.min.js b/apps/web-antd/public/tinymce/plugins/codesample/plugin.min.js new file mode 100644 index 0000000..dfb812a --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/codesample/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>!(e=>null==e)(e),n=()=>{};class a{constructor(e,t){this.tag=e,this.value=t}static some(e){return new a(!0,e)}static none(){return a.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?a.some(e(this.value)):a.none()}bind(e){return this.tag?e(this.value):a.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:a.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return t(e)?a.some(e):a.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}a.singletonNone=new a(!1);var s=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils");const r="undefined"!=typeof window?window:Function("return this;")(),i=function(e,t,n){const a=window.Prism;window.Prism={manual:!0};var s=function(e){var t=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,n=0,a={},s={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function e(t){return t instanceof r?new r(t.type,e(t.content),t.alias):Array.isArray(t)?t.map(e):t.replace(/&/g,"&").replace(/=d.reach);x+=_.value.length,_=_.next){var F=_.value;if(t.length>e.length)return;if(!(F instanceof r)){var A,S=1;if(y){if(!(A=i(v,x,e,m))||A.index>=e.length)break;var $=A.index,z=A.index+A[0].length,E=x;for(E+=_.value.length;$>=E;)E+=(_=_.next).value.length;if(x=E-=_.value.length,_.value instanceof r)continue;for(var C=_;C!==t.tail&&(Ed.reach&&(d.reach=O);var P=_.prev;if(B&&(P=u(t,P,B),x+=B.length),c(t,P,S),_=u(t,P,new r(g,f?s.tokenize(j,f):j,w,j)),T&&u(t,_,T),S>1){var N={cause:g+","+b,reach:O};o(e,t,n,_.prev,x,N),d&&N.reach>d.reach&&(d.reach=N.reach)}}}}}}function l(){var e={value:null,prev:null,next:null},t={value:null,prev:e,next:null};e.next=t,this.head=e,this.tail=t,this.length=0}function u(e,t,n){var a=t.next,s={value:n,prev:t,next:a};return t.next=s,a.prev=s,e.length++,s}function c(e,t,n){for(var a=t.next,s=0;s"+r.content+""},!e.document)return e.addEventListener?(s.disableWorkerMessageHandler||e.addEventListener("message",(function(t){var n=JSON.parse(t.data),a=n.language,r=n.code,i=n.immediateClose;e.postMessage(s.highlight(r,s.languages[a],a)),i&&e.close()}),!1),s):s;var d=s.util.currentScript();function g(){s.manual||s.highlightAll()}if(d&&(s.filename=d.src,d.hasAttribute("data-manual")&&(s.manual=!0)),!s.manual){var p=document.readyState;"loading"===p||"interactive"===p&&d&&d.defer?document.addEventListener("DOMContentLoaded",g):window.requestAnimationFrame?window.requestAnimationFrame(g):window.setTimeout(g,16)}return s}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{});return s.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},function(e){function t(e,t){return"___"+e.toUpperCase()+t+"___"}Object.defineProperties(e.languages["markup-templating"]={},{buildPlaceholders:{value:function(n,a,s,r){if(n.language===a){var i=n.tokenStack=[];n.code=n.code.replace(s,(function(e){if("function"==typeof r&&!r(e))return e;for(var s,o=i.length;-1!==n.code.indexOf(s=t(a,o));)++o;return i[o]=e,s})),n.grammar=e.languages.markup}}},tokenizePlaceholders:{value:function(n,a){if(n.language===a&&n.tokenStack){n.grammar=e.languages[a];var s=0,r=Object.keys(n.tokenStack);!function i(o){for(var l=0;l=r.length);l++){var u=o[l];if("string"==typeof u||u.content&&"string"==typeof u.content){var c=r[s],d=n.tokenStack[c],g="string"==typeof u?u:u.content,p=t(a,c),b=g.indexOf(p);if(b>-1){++s;var h=g.substring(0,b),f=new e.Token(a,e.tokenize(d,n.grammar),"language-"+a,d),m=g.substring(b+p.length),y=[];h&&y.push.apply(y,i([h])),y.push(f),m&&y.push.apply(y,i([m])),"string"==typeof u?o.splice.apply(o,[l,1].concat(y)):u.content=y}}else u.content&&i(u.content)}return o}(n.tokens)}}}})}(s),s.languages.c=s.languages.extend("clike",{comment:{pattern:/\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},"class-name":{pattern:/(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/,lookbehind:!0},keyword:/\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\b/,function:/\b[a-z_]\w*(?=\s*\()/i,number:/(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i,operator:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/}),s.languages.insertBefore("c","string",{char:{pattern:/'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/,greedy:!0}}),s.languages.insertBefore("c","string",{macro:{pattern:/(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},s.languages.c.string],char:s.languages.c.char,comment:s.languages.c.comment,"macro-name":[{pattern:/(^#\s*define\s+)\w+\b(?!\()/i,lookbehind:!0},{pattern:/(^#\s*define\s+)\w+\b(?=\()/i,lookbehind:!0,alias:"function"}],directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:s.languages.c}}}}),s.languages.insertBefore("c","function",{constant:/\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/}),delete s.languages.c.boolean,function(e){var t=/\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,n=/\b(?!)\w+(?:\s*\.\s*\w+)*\b/.source.replace(//g,(function(){return t.source}));e.languages.cpp=e.languages.extend("c",{"class-name":[{pattern:RegExp(/(\b(?:class|concept|enum|struct|typename)\s+)(?!)\w+/.source.replace(//g,(function(){return t.source}))),lookbehind:!0},/\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,/\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,/\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/],keyword:t,number:{pattern:/(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i,greedy:!0},operator:/>>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,boolean:/\b(?:false|true)\b/}),e.languages.insertBefore("cpp","string",{module:{pattern:RegExp(/(\b(?:import|module)\s+)/.source+"(?:"+/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|<[^<>\r\n]*>/.source+"|"+/(?:\s*:\s*)?|:\s*/.source.replace(//g,(function(){return n}))+")"),lookbehind:!0,greedy:!0,inside:{string:/^[<"][\s\S]+/,operator:/:/,punctuation:/\./}},"raw-string":{pattern:/R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,alias:"string",greedy:!0}}),e.languages.insertBefore("cpp","keyword",{"generic-function":{pattern:/\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i,inside:{function:/^\w+/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:e.languages.cpp}}}}),e.languages.insertBefore("cpp","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}}),e.languages.insertBefore("cpp","class-name",{"base-clause":{pattern:/(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:e.languages.extend("cpp",{})}}),e.languages.insertBefore("inside","double-colon",{"class-name":/\b[a-z_]\w*\b(?!\s*::)/i},e.languages.cpp["base-clause"])}(s),function(e){function t(e,t){return e.replace(/<<(\d+)>>/g,(function(e,n){return"(?:"+t[+n]+")"}))}function n(e,n,a){return RegExp(t(e,n),a||"")}function a(e,t){for(var n=0;n>/g,(function(){return"(?:"+e+")"}));return e.replace(/<>/g,"[^\\s\\S]")}var s="bool byte char decimal double dynamic float int long object sbyte short string uint ulong ushort var void",r="class enum interface record struct",i="add alias and ascending async await by descending from(?=\\s*(?:\\w|$)) get global group into init(?=\\s*;) join let nameof not notnull on or orderby partial remove select set unmanaged value when where with(?=\\s*{)",o="abstract as base break case catch checked const continue default delegate do else event explicit extern finally fixed for foreach goto if implicit in internal is lock namespace new null operator out override params private protected public readonly ref return sealed sizeof stackalloc static switch this throw try typeof unchecked unsafe using virtual volatile while yield";function l(e){return"\\b(?:"+e.trim().replace(/ /g,"|")+")\\b"}var u=l(r),c=RegExp(l(s+" "+r+" "+i+" "+o)),d=l(r+" "+i+" "+o),g=l(s+" "+r+" "+o),p=a(/<(?:[^<>;=+\-*/%&|^]|<>)*>/.source,2),b=a(/\((?:[^()]|<>)*\)/.source,2),h=/@?\b[A-Za-z_]\w*\b/.source,f=t(/<<0>>(?:\s*<<1>>)?/.source,[h,p]),m=t(/(?!<<0>>)<<1>>(?:\s*\.\s*<<1>>)*/.source,[d,f]),y=/\[\s*(?:,\s*)*\]/.source,w=t(/<<0>>(?:\s*(?:\?\s*)?<<1>>)*(?:\s*\?)?/.source,[m,y]),k=t(/[^,()<>[\];=+\-*/%&|^]|<<0>>|<<1>>|<<2>>/.source,[p,b,y]),v=t(/\(<<0>>+(?:,<<0>>+)+\)/.source,[k]),_=t(/(?:<<0>>|<<1>>)(?:\s*(?:\?\s*)?<<2>>)*(?:\s*\?)?/.source,[v,m,y]),x={keyword:c,punctuation:/[<>()?,.:[\]]/},F=/'(?:[^\r\n'\\]|\\.|\\[Uux][\da-fA-F]{1,8})'/.source,A=/"(?:\\.|[^\\"\r\n])*"/.source,S=/@"(?:""|\\[\s\S]|[^\\"])*"(?!")/.source;e.languages.csharp=e.languages.extend("clike",{string:[{pattern:n(/(^|[^$\\])<<0>>/.source,[S]),lookbehind:!0,greedy:!0},{pattern:n(/(^|[^@$\\])<<0>>/.source,[A]),lookbehind:!0,greedy:!0}],"class-name":[{pattern:n(/(\busing\s+static\s+)<<0>>(?=\s*;)/.source,[m]),lookbehind:!0,inside:x},{pattern:n(/(\busing\s+<<0>>\s*=\s*)<<1>>(?=\s*;)/.source,[h,_]),lookbehind:!0,inside:x},{pattern:n(/(\busing\s+)<<0>>(?=\s*=)/.source,[h]),lookbehind:!0},{pattern:n(/(\b<<0>>\s+)<<1>>/.source,[u,f]),lookbehind:!0,inside:x},{pattern:n(/(\bcatch\s*\(\s*)<<0>>/.source,[m]),lookbehind:!0,inside:x},{pattern:n(/(\bwhere\s+)<<0>>/.source,[h]),lookbehind:!0},{pattern:n(/(\b(?:is(?:\s+not)?|as)\s+)<<0>>/.source,[w]),lookbehind:!0,inside:x},{pattern:n(/\b<<0>>(?=\s+(?!<<1>>|with\s*\{)<<2>>(?:\s*[=,;:{)\]]|\s+(?:in|when)\b))/.source,[_,g,h]),inside:x}],keyword:c,number:/(?:\b0(?:x[\da-f_]*[\da-f]|b[01_]*[01])|(?:\B\.\d+(?:_+\d+)*|\b\d+(?:_+\d+)*(?:\.\d+(?:_+\d+)*)?)(?:e[-+]?\d+(?:_+\d+)*)?)(?:[dflmu]|lu|ul)?\b/i,operator:/>>=?|<<=?|[-=]>|([-+&|])\1|~|\?\?=?|[-+*/%&|^!=<>]=?/,punctuation:/\?\.?|::|[{}[\];(),.:]/}),e.languages.insertBefore("csharp","number",{range:{pattern:/\.\./,alias:"operator"}}),e.languages.insertBefore("csharp","punctuation",{"named-parameter":{pattern:n(/([(,]\s*)<<0>>(?=\s*:)/.source,[h]),lookbehind:!0,alias:"punctuation"}}),e.languages.insertBefore("csharp","class-name",{namespace:{pattern:n(/(\b(?:namespace|using)\s+)<<0>>(?:\s*\.\s*<<0>>)*(?=\s*[;{])/.source,[h]),lookbehind:!0,inside:{punctuation:/\./}},"type-expression":{pattern:n(/(\b(?:default|sizeof|typeof)\s*\(\s*(?!\s))(?:[^()\s]|\s(?!\s)|<<0>>)*(?=\s*\))/.source,[b]),lookbehind:!0,alias:"class-name",inside:x},"return-type":{pattern:n(/<<0>>(?=\s+(?:<<1>>\s*(?:=>|[({]|\.\s*this\s*\[)|this\s*\[))/.source,[_,m]),inside:x,alias:"class-name"},"constructor-invocation":{pattern:n(/(\bnew\s+)<<0>>(?=\s*[[({])/.source,[_]),lookbehind:!0,inside:x,alias:"class-name"},"generic-method":{pattern:n(/<<0>>\s*<<1>>(?=\s*\()/.source,[h,p]),inside:{function:n(/^<<0>>/.source,[h]),generic:{pattern:RegExp(p),alias:"class-name",inside:x}}},"type-list":{pattern:n(/\b((?:<<0>>\s+<<1>>|record\s+<<1>>\s*<<5>>|where\s+<<2>>)\s*:\s*)(?:<<3>>|<<4>>|<<1>>\s*<<5>>|<<6>>)(?:\s*,\s*(?:<<3>>|<<4>>|<<6>>))*(?=\s*(?:where|[{;]|=>|$))/.source,[u,f,h,_,c.source,b,/\bnew\s*\(\s*\)/.source]),lookbehind:!0,inside:{"record-arguments":{pattern:n(/(^(?!new\s*\()<<0>>\s*)<<1>>/.source,[f,b]),lookbehind:!0,greedy:!0,inside:e.languages.csharp},keyword:c,"class-name":{pattern:RegExp(_),greedy:!0,inside:x},punctuation:/[,()]/}},preprocessor:{pattern:/(^[\t ]*)#.*/m,lookbehind:!0,alias:"property",inside:{directive:{pattern:/(#)\b(?:define|elif|else|endif|endregion|error|if|line|nullable|pragma|region|undef|warning)\b/,lookbehind:!0,alias:"keyword"}}}});var $=A+"|"+F,z=t(/\/(?![*/])|\/\/[^\r\n]*[\r\n]|\/\*(?:[^*]|\*(?!\/))*\*\/|<<0>>/.source,[$]),E=a(t(/[^"'/()]|<<0>>|\(<>*\)/.source,[z]),2),C=/\b(?:assembly|event|field|method|module|param|property|return|type)\b/.source,j=t(/<<0>>(?:\s*\(<<1>>*\))?/.source,[m,E]);e.languages.insertBefore("csharp","class-name",{attribute:{pattern:n(/((?:^|[^\s\w>)?])\s*\[\s*)(?:<<0>>\s*:\s*)?<<1>>(?:\s*,\s*<<1>>)*(?=\s*\])/.source,[C,j]),lookbehind:!0,greedy:!0,inside:{target:{pattern:n(/^<<0>>(?=\s*:)/.source,[C]),alias:"keyword"},"attribute-arguments":{pattern:n(/\(<<0>>*\)/.source,[E]),inside:e.languages.csharp},"class-name":{pattern:RegExp(m),inside:{punctuation:/\./}},punctuation:/[:,]/}}});var B=/:[^}\r\n]+/.source,T=a(t(/[^"'/()]|<<0>>|\(<>*\)/.source,[z]),2),O=t(/\{(?!\{)(?:(?![}:])<<0>>)*<<1>>?\}/.source,[T,B]),P=a(t(/[^"'/()]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|<<0>>|\(<>*\)/.source,[$]),2),N=t(/\{(?!\{)(?:(?![}:])<<0>>)*<<1>>?\}/.source,[P,B]);function R(t,a){return{interpolation:{pattern:n(/((?:^|[^{])(?:\{\{)*)<<0>>/.source,[t]),lookbehind:!0,inside:{"format-string":{pattern:n(/(^\{(?:(?![}:])<<0>>)*)<<1>>(?=\}$)/.source,[a,B]),lookbehind:!0,inside:{punctuation:/^:/}},punctuation:/^\{|\}$/,expression:{pattern:/[\s\S]+/,alias:"language-csharp",inside:e.languages.csharp}}},string:/[\s\S]+/}}e.languages.insertBefore("csharp","string",{"interpolation-string":[{pattern:n(/(^|[^\\])(?:\$@|@\$)"(?:""|\\[\s\S]|\{\{|<<0>>|[^\\{"])*"/.source,[O]),lookbehind:!0,greedy:!0,inside:R(O,T)},{pattern:n(/(^|[^@\\])\$"(?:\\.|\{\{|<<0>>|[^\\"{])*"/.source,[N]),lookbehind:!0,greedy:!0,inside:R(N,P)}],char:{pattern:RegExp(F),greedy:!0}}),e.languages.dotnet=e.languages.cs=e.languages.csharp}(s),function(e){var t=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:RegExp("@[\\w-](?:"+/[^;{\s"']|\s+(?!\s)/.source+"|"+t.source+")*?"+/(?:;|(?=\s*\{))/.source),inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+t.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+t.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+t.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:t,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var n=e.languages.markup;n&&(n.tag.addInlined("style","css"),n.tag.addAttribute("style","css"))}(s),function(e){var t=/\b(?:abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|exports|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|module|native|new|non-sealed|null|open|opens|package|permits|private|protected|provides|public|record(?!\s*[(){}[\]<>=%~.:,;?+\-*/&|^])|requires|return|sealed|short|static|strictfp|super|switch|synchronized|this|throw|throws|to|transient|transitive|try|uses|var|void|volatile|while|with|yield)\b/,n=/(?:[a-z]\w*\s*\.\s*)*(?:[A-Z]\w*\s*\.\s*)*/.source,a={pattern:RegExp(/(^|[^\w.])/.source+n+/[A-Z](?:[\d_A-Z]*[a-z]\w*)?\b/.source),lookbehind:!0,inside:{namespace:{pattern:/^[a-z]\w*(?:\s*\.\s*[a-z]\w*)*(?:\s*\.)?/,inside:{punctuation:/\./}},punctuation:/\./}};e.languages.java=e.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"/,lookbehind:!0,greedy:!0},"class-name":[a,{pattern:RegExp(/(^|[^\w.])/.source+n+/[A-Z]\w*(?=\s+\w+\s*[;,=()]|\s*(?:\[[\s,]*\]\s*)?::\s*new\b)/.source),lookbehind:!0,inside:a.inside},{pattern:RegExp(/(\b(?:class|enum|extends|implements|instanceof|interface|new|record|throws)\s+)/.source+n+/[A-Z]\w*\b/.source),lookbehind:!0,inside:a.inside}],keyword:t,function:[e.languages.clike.function,{pattern:/(::\s*)[a-z_]\w*/,lookbehind:!0}],number:/\b0b[01][01_]*L?\b|\b0x(?:\.[\da-f_p+-]+|[\da-f_]+(?:\.[\da-f_p+-]+)?)\b|(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?\d[\d_]*)?[dfl]?/i,operator:{pattern:/(^|[^.])(?:<<=?|>>>?=?|->|--|\+\+|&&|\|\||::|[?:~]|[-+*/%&|^!=<>]=?)/m,lookbehind:!0},constant:/\b[A-Z][A-Z_\d]+\b/}),e.languages.insertBefore("java","string",{"triple-quoted-string":{pattern:/"""[ \t]*[\r\n](?:(?:"|"")?(?:\\.|[^"\\]))*"""/,greedy:!0,alias:"string"},char:{pattern:/'(?:\\.|[^'\\\r\n]){1,6}'/,greedy:!0}}),e.languages.insertBefore("java","class-name",{annotation:{pattern:/(^|[^.])@\w+(?:\s*\.\s*\w+)*/,lookbehind:!0,alias:"punctuation"},generics:{pattern:/<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&))*>)*>)*>)*>/,inside:{"class-name":a,keyword:t,punctuation:/[<>(),.:]/,operator:/[?&|]/}},import:[{pattern:RegExp(/(\bimport\s+)/.source+n+/(?:[A-Z]\w*|\*)(?=\s*;)/.source),lookbehind:!0,inside:{namespace:a.inside.namespace,punctuation:/\./,operator:/\*/,"class-name":/\w+/}},{pattern:RegExp(/(\bimport\s+static\s+)/.source+n+/(?:\w+|\*)(?=\s*;)/.source),lookbehind:!0,alias:"static",inside:{namespace:a.inside.namespace,static:/\b\w+$/,punctuation:/\./,operator:/\*/,"class-name":/\w+/}}],namespace:{pattern:RegExp(/(\b(?:exports|import(?:\s+static)?|module|open|opens|package|provides|requires|to|transitive|uses|with)\s+)(?!)[a-z]\w*(?:\.[a-z]\w*)*\.?/.source.replace(//g,(function(){return t.source}))),lookbehind:!0,inside:{punctuation:/\./}}})}(s),s.languages.javascript=s.languages.extend("clike",{"class-name":[s.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),s.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,s.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp(/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)/.source+/\//.source+"(?:"+/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}/.source+"|"+/(?:\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.)*\])*\])*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}v[dgimyus]{0,7}/.source+")"+/(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/.source),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:s.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:s.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:s.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:s.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:s.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),s.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:s.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),s.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),s.languages.markup&&(s.languages.markup.tag.addInlined("script","javascript"),s.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),s.languages.js=s.languages.javascript,s.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},s.languages.markup.tag.inside["attr-value"].inside.entity=s.languages.markup.entity,s.languages.markup.doctype.inside["internal-subset"].inside=s.languages.markup,s.hooks.add("wrap",(function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))})),Object.defineProperty(s.languages.markup.tag,"addInlined",{value:function(e,t){var n={};n["language-"+t]={pattern:/(^$)/i,lookbehind:!0,inside:s.languages[t]},n.cdata=/^$/i;var a={"included-cdata":{pattern://i,inside:n}};a["language-"+t]={pattern:/[\s\S]+/,inside:s.languages[t]};var r={};r[e]={pattern:RegExp(/(<__[^>]*>)(?:))*\]\]>|(?!)/.source.replace(/__/g,(function(){return e})),"i"),lookbehind:!0,greedy:!0,inside:a},s.languages.insertBefore("markup","cdata",r)}}),Object.defineProperty(s.languages.markup.tag,"addAttribute",{value:function(e,t){s.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[t,"language-"+t],inside:s.languages[t]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),s.languages.html=s.languages.markup,s.languages.mathml=s.languages.markup,s.languages.svg=s.languages.markup,s.languages.xml=s.languages.extend("markup",{}),s.languages.ssml=s.languages.xml,s.languages.atom=s.languages.xml,s.languages.rss=s.languages.xml,function(e){var t=/\/\*[\s\S]*?\*\/|\/\/.*|#(?!\[).*/,n=[{pattern:/\b(?:false|true)\b/i,alias:"boolean"},{pattern:/(::\s*)\b[a-z_]\w*\b(?!\s*\()/i,greedy:!0,lookbehind:!0},{pattern:/(\b(?:case|const)\s+)\b[a-z_]\w*(?=\s*[;=])/i,greedy:!0,lookbehind:!0},/\b(?:null)\b/i,/\b[A-Z_][A-Z0-9_]*\b(?!\s*\()/],a=/\b0b[01]+(?:_[01]+)*\b|\b0o[0-7]+(?:_[0-7]+)*\b|\b0x[\da-f]+(?:_[\da-f]+)*\b|(?:\b\d+(?:_\d+)*\.?(?:\d+(?:_\d+)*)?|\B\.\d+)(?:e[+-]?\d+)?/i,s=/|\?\?=?|\.{3}|\??->|[!=]=?=?|::|\*\*=?|--|\+\+|&&|\|\||<<|>>|[?~]|[/^|%*&<>.+-]=?/,r=/[{}\[\](),:;]/;e.languages.php={delimiter:{pattern:/\?>$|^<\?(?:php(?=\s)|=)?/i,alias:"important"},comment:t,variable:/\$+(?:\w+\b|(?=\{))/,package:{pattern:/(namespace\s+|use\s+(?:function\s+)?)(?:\\?\b[a-z_]\w*)+\b(?!\\)/i,lookbehind:!0,inside:{punctuation:/\\/}},"class-name-definition":{pattern:/(\b(?:class|enum|interface|trait)\s+)\b[a-z_]\w*(?!\\)\b/i,lookbehind:!0,alias:"class-name"},"function-definition":{pattern:/(\bfunction\s+)[a-z_]\w*(?=\s*\()/i,lookbehind:!0,alias:"function"},keyword:[{pattern:/(\(\s*)\b(?:array|bool|boolean|float|int|integer|object|string)\b(?=\s*\))/i,alias:"type-casting",greedy:!0,lookbehind:!0},{pattern:/([(,?]\s*)\b(?:array(?!\s*\()|bool|callable|(?:false|null)(?=\s*\|)|float|int|iterable|mixed|object|self|static|string)\b(?=\s*\$)/i,alias:"type-hint",greedy:!0,lookbehind:!0},{pattern:/(\)\s*:\s*(?:\?\s*)?)\b(?:array(?!\s*\()|bool|callable|(?:false|null)(?=\s*\|)|float|int|iterable|mixed|never|object|self|static|string|void)\b/i,alias:"return-type",greedy:!0,lookbehind:!0},{pattern:/\b(?:array(?!\s*\()|bool|float|int|iterable|mixed|object|string|void)\b/i,alias:"type-declaration",greedy:!0},{pattern:/(\|\s*)(?:false|null)\b|\b(?:false|null)(?=\s*\|)/i,alias:"type-declaration",greedy:!0,lookbehind:!0},{pattern:/\b(?:parent|self|static)(?=\s*::)/i,alias:"static-context",greedy:!0},{pattern:/(\byield\s+)from\b/i,lookbehind:!0},/\bclass\b/i,{pattern:/((?:^|[^\s>:]|(?:^|[^-])>|(?:^|[^:]):)\s*)\b(?:abstract|and|array|as|break|callable|case|catch|clone|const|continue|declare|default|die|do|echo|else|elseif|empty|enddeclare|endfor|endforeach|endif|endswitch|endwhile|enum|eval|exit|extends|final|finally|fn|for|foreach|function|global|goto|if|implements|include|include_once|instanceof|insteadof|interface|isset|list|match|namespace|never|new|or|parent|print|private|protected|public|readonly|require|require_once|return|self|static|switch|throw|trait|try|unset|use|var|while|xor|yield|__halt_compiler)\b/i,lookbehind:!0}],"argument-name":{pattern:/([(,]\s*)\b[a-z_]\w*(?=\s*:(?!:))/i,lookbehind:!0},"class-name":[{pattern:/(\b(?:extends|implements|instanceof|new(?!\s+self|\s+static))\s+|\bcatch\s*\()\b[a-z_]\w*(?!\\)\b/i,greedy:!0,lookbehind:!0},{pattern:/(\|\s*)\b[a-z_]\w*(?!\\)\b/i,greedy:!0,lookbehind:!0},{pattern:/\b[a-z_]\w*(?!\\)\b(?=\s*\|)/i,greedy:!0},{pattern:/(\|\s*)(?:\\?\b[a-z_]\w*)+\b/i,alias:"class-name-fully-qualified",greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}},{pattern:/(?:\\?\b[a-z_]\w*)+\b(?=\s*\|)/i,alias:"class-name-fully-qualified",greedy:!0,inside:{punctuation:/\\/}},{pattern:/(\b(?:extends|implements|instanceof|new(?!\s+self\b|\s+static\b))\s+|\bcatch\s*\()(?:\\?\b[a-z_]\w*)+\b(?!\\)/i,alias:"class-name-fully-qualified",greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}},{pattern:/\b[a-z_]\w*(?=\s*\$)/i,alias:"type-declaration",greedy:!0},{pattern:/(?:\\?\b[a-z_]\w*)+(?=\s*\$)/i,alias:["class-name-fully-qualified","type-declaration"],greedy:!0,inside:{punctuation:/\\/}},{pattern:/\b[a-z_]\w*(?=\s*::)/i,alias:"static-context",greedy:!0},{pattern:/(?:\\?\b[a-z_]\w*)+(?=\s*::)/i,alias:["class-name-fully-qualified","static-context"],greedy:!0,inside:{punctuation:/\\/}},{pattern:/([(,?]\s*)[a-z_]\w*(?=\s*\$)/i,alias:"type-hint",greedy:!0,lookbehind:!0},{pattern:/([(,?]\s*)(?:\\?\b[a-z_]\w*)+(?=\s*\$)/i,alias:["class-name-fully-qualified","type-hint"],greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}},{pattern:/(\)\s*:\s*(?:\?\s*)?)\b[a-z_]\w*(?!\\)\b/i,alias:"return-type",greedy:!0,lookbehind:!0},{pattern:/(\)\s*:\s*(?:\?\s*)?)(?:\\?\b[a-z_]\w*)+\b(?!\\)/i,alias:["class-name-fully-qualified","return-type"],greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}}],constant:n,function:{pattern:/(^|[^\\\w])\\?[a-z_](?:[\w\\]*\w)?(?=\s*\()/i,lookbehind:!0,inside:{punctuation:/\\/}},property:{pattern:/(->\s*)\w+/,lookbehind:!0},number:a,operator:s,punctuation:r};var i={pattern:/\{\$(?:\{(?:\{[^{}]+\}|[^{}]+)\}|[^{}])+\}|(^|[^\\{])\$+(?:\w+(?:\[[^\r\n\[\]]+\]|->\w+)?)/,lookbehind:!0,inside:e.languages.php},o=[{pattern:/<<<'([^']+)'[\r\n](?:.*[\r\n])*?\1;/,alias:"nowdoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<<'[^']+'|[a-z_]\w*;$/i,alias:"symbol",inside:{punctuation:/^<<<'?|[';]$/}}}},{pattern:/<<<(?:"([^"]+)"[\r\n](?:.*[\r\n])*?\1;|([a-z_]\w*)[\r\n](?:.*[\r\n])*?\2;)/i,alias:"heredoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<<(?:"[^"]+"|[a-z_]\w*)|[a-z_]\w*;$/i,alias:"symbol",inside:{punctuation:/^<<<"?|[";]$/}},interpolation:i}},{pattern:/`(?:\\[\s\S]|[^\\`])*`/,alias:"backtick-quoted-string",greedy:!0},{pattern:/'(?:\\[\s\S]|[^\\'])*'/,alias:"single-quoted-string",greedy:!0},{pattern:/"(?:\\[\s\S]|[^\\"])*"/,alias:"double-quoted-string",greedy:!0,inside:{interpolation:i}}];e.languages.insertBefore("php","variable",{string:o,attribute:{pattern:/#\[(?:[^"'\/#]|\/(?![*/])|\/\/.*$|#(?!\[).*$|\/\*(?:[^*]|\*(?!\/))*\*\/|"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*')+\](?=\s*[a-z$#])/im,greedy:!0,inside:{"attribute-content":{pattern:/^(#\[)[\s\S]+(?=\]$)/,lookbehind:!0,inside:{comment:t,string:o,"attribute-class-name":[{pattern:/([^:]|^)\b[a-z_]\w*(?!\\)\b/i,alias:"class-name",greedy:!0,lookbehind:!0},{pattern:/([^:]|^)(?:\\?\b[a-z_]\w*)+/i,alias:["class-name","class-name-fully-qualified"],greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}}],constant:n,number:a,operator:s,punctuation:r}},delimiter:{pattern:/^#\[|\]$/,alias:"punctuation"}}}}),e.hooks.add("before-tokenize",(function(t){/<\?/.test(t.code)&&e.languages["markup-templating"].buildPlaceholders(t,"php",/<\?(?:[^"'/#]|\/(?![*/])|("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|(?:\/\/|#(?!\[))(?:[^?\n\r]|\?(?!>))*(?=$|\?>|[\r\n])|#\[|\/\*(?:[^*]|\*(?!\/))*(?:\*\/|$))*?(?:\?>|$)/g)})),e.hooks.add("after-tokenize",(function(t){e.languages["markup-templating"].tokenizePlaceholders(t,"php")}))}(s),s.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},s.languages.python["string-interpolation"].inside.interpolation.inside.rest=s.languages.python,s.languages.py=s.languages.python,function(e){e.languages.ruby=e.languages.extend("clike",{comment:{pattern:/#.*|^=begin\s[\s\S]*?^=end/m,greedy:!0},"class-name":{pattern:/(\b(?:class|module)\s+|\bcatch\s+\()[\w.\\]+|\b[A-Z_]\w*(?=\s*\.\s*new\b)/,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:BEGIN|END|alias|and|begin|break|case|class|def|define_method|defined|do|each|else|elsif|end|ensure|extend|for|if|in|include|module|new|next|nil|not|or|prepend|private|protected|public|raise|redo|require|rescue|retry|return|self|super|then|throw|undef|unless|until|when|while|yield)\b/,operator:/\.{2,3}|&\.|===||[!=]?~|(?:&&|\|\||<<|>>|\*\*|[+\-*/%<>!^&|=])=?|[?:]/,punctuation:/[(){}[\].,;]/}),e.languages.insertBefore("ruby","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}});var t={pattern:/((?:^|[^\\])(?:\\{2})*)#\{(?:[^{}]|\{[^{}]*\})*\}/,lookbehind:!0,inside:{content:{pattern:/^(#\{)[\s\S]+(?=\}$)/,lookbehind:!0,inside:e.languages.ruby},delimiter:{pattern:/^#\{|\}$/,alias:"punctuation"}}};delete e.languages.ruby.function;var n="(?:"+[/([^a-zA-Z0-9\s{(\[<=])(?:(?!\1)[^\\]|\\[\s\S])*\1/.source,/\((?:[^()\\]|\\[\s\S]|\((?:[^()\\]|\\[\s\S])*\))*\)/.source,/\{(?:[^{}\\]|\\[\s\S]|\{(?:[^{}\\]|\\[\s\S])*\})*\}/.source,/\[(?:[^\[\]\\]|\\[\s\S]|\[(?:[^\[\]\\]|\\[\s\S])*\])*\]/.source,/<(?:[^<>\\]|\\[\s\S]|<(?:[^<>\\]|\\[\s\S])*>)*>/.source].join("|")+")",a=/(?:"(?:\\.|[^"\\\r\n])*"|(?:\b[a-zA-Z_]\w*|[^\s\0-\x7F]+)[?!]?|\$.)/.source;e.languages.insertBefore("ruby","keyword",{"regex-literal":[{pattern:RegExp(/%r/.source+n+/[egimnosux]{0,6}/.source),greedy:!0,inside:{interpolation:t,regex:/[\s\S]+/}},{pattern:/(^|[^/])\/(?!\/)(?:\[[^\r\n\]]+\]|\\.|[^[/\\\r\n])+\/[egimnosux]{0,6}(?=\s*(?:$|[\r\n,.;})#]))/,lookbehind:!0,greedy:!0,inside:{interpolation:t,regex:/[\s\S]+/}}],variable:/[@$]+[a-zA-Z_]\w*(?:[?!]|\b)/,symbol:[{pattern:RegExp(/(^|[^:]):/.source+a),lookbehind:!0,greedy:!0},{pattern:RegExp(/([\r\n{(,][ \t]*)/.source+a+/(?=:(?!:))/.source),lookbehind:!0,greedy:!0}],"method-definition":{pattern:/(\bdef\s+)\w+(?:\s*\.\s*\w+)?/,lookbehind:!0,inside:{function:/\b\w+$/,keyword:/^self\b/,"class-name":/^\w+/,punctuation:/\./}}}),e.languages.insertBefore("ruby","string",{"string-literal":[{pattern:RegExp(/%[qQiIwWs]?/.source+n),greedy:!0,inside:{interpolation:t,string:/[\s\S]+/}},{pattern:/("|')(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|(?!\1)[^\\#\r\n])*\1/,greedy:!0,inside:{interpolation:t,string:/[\s\S]+/}},{pattern:/<<[-~]?([a-z_]\w*)[\r\n](?:.*[\r\n])*?[\t ]*\1/i,alias:"heredoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<[-~]?[a-z_]\w*|\b[a-z_]\w*$/i,inside:{symbol:/\b\w+/,punctuation:/^<<[-~]?/}},interpolation:t,string:/[\s\S]+/}},{pattern:/<<[-~]?'([a-z_]\w*)'[\r\n](?:.*[\r\n])*?[\t ]*\1/i,alias:"heredoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<[-~]?'[a-z_]\w*'|\b[a-z_]\w*$/i,inside:{symbol:/\b\w+/,punctuation:/^<<[-~]?'|'$/}},string:/[\s\S]+/}}],"command-literal":[{pattern:RegExp(/%x/.source+n),greedy:!0,inside:{interpolation:t,command:{pattern:/[\s\S]+/,alias:"string"}}},{pattern:/`(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|[^\\`#\r\n])*`/,greedy:!0,inside:{interpolation:t,command:{pattern:/[\s\S]+/,alias:"string"}}}]}),delete e.languages.ruby.string,e.languages.insertBefore("ruby","number",{builtin:/\b(?:Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Fixnum|Float|Hash|IO|Integer|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|Stat|String|Struct|Symbol|TMS|Thread|ThreadGroup|Time|TrueClass)\b/,constant:/\b[A-Z][A-Z0-9_]*(?:[?!]|\b)/}),e.languages.rb=e.languages.ruby}(s),window.Prism=a,s}(),o=e=>t=>t.options.get(e),l=o("codesample_languages"),u=o("codesample_global_prismjs"),c=e=>r.Prism&&u(e)?r.Prism:i,d=e=>t(e)&&"PRE"===e.nodeName&&-1!==e.className.indexOf("language-"),g=e=>{const t=e.selection?e.selection.getNode():null;return d(t)?a.some(t):a.none()},p=e=>{const t=(e=>l(e)||[{text:"HTML/XML",value:"markup"},{text:"JavaScript",value:"javascript"},{text:"CSS",value:"css"},{text:"PHP",value:"php"},{text:"Ruby",value:"ruby"},{text:"Python",value:"python"},{text:"Java",value:"java"},{text:"C",value:"c"},{text:"C#",value:"csharp"},{text:"C++",value:"cpp"}])(e),n=(r=t,((e,t)=>0""),(e=>e.value));var r;const i=((e,t)=>g(e).fold((()=>t),(e=>{const n=e.className.match(/language-(\w+)/);return n?n[1]:t})))(e,n),o=(e=>g(e).bind((e=>a.from(e.textContent))).getOr(""))(e);e.windowManager.open({title:"Insert/Edit Code Sample",size:"large",body:{type:"panel",items:[{type:"listbox",name:"language",label:"Language",items:t},{type:"textarea",name:"code",label:"Code view"}]},buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],initialData:{language:i,code:o},onSubmit:t=>{const n=t.getData();((e,t,n)=>{const a=e.dom;e.undoManager.transact((()=>{const r=g(e);return n=s.DOM.encode(n),r.fold((()=>{e.insertContent('
'+n+"
");const s=a.select("#__new")[0];a.setAttrib(s,"id",null),e.selection.select(s)}),(s=>{a.setAttrib(s,"class","language-"+t),s.innerHTML=n,c(e).highlightElement(s),e.selection.select(s)}))}))})(e,n.language,n.code),t.close()}})},b=(h=/^\s+|\s+$/g,e=>e.replace(h,""));var h,f=tinymce.util.Tools.resolve("tinymce.util.Tools");const m=(e,t=n)=>n=>{const a=()=>{n.setEnabled(e.selection.isEditable()),t(n)};return e.on("NodeChange",a),a(),()=>{e.off("NodeChange",a)}};e.add("codesample",(e=>{(e=>{const t=e.options.register;t("codesample_languages",{processor:"object[]"}),t("codesample_global_prismjs",{processor:"boolean",default:!1})})(e),(e=>{e.on("PreProcess",(t=>{const n=e.dom,a=n.select("pre[contenteditable=false]",t.node);f.each(f.grep(a,d),(e=>{const t=e.textContent;let a;for(n.setAttrib(e,"class",b(n.getAttrib(e,"class"))),n.setAttrib(e,"contentEditable",null),n.setAttrib(e,"data-mce-highlighted",null);a=e.firstChild;)e.removeChild(a);n.add(e,"code").textContent=t}))})),e.on("SetContent",(()=>{const t=e.dom,n=f.grep(t.select("pre"),(e=>d(e)&&"true"!==t.getAttrib(e,"data-mce-highlighted")));n.length&&e.undoManager.transact((()=>{f.each(n,(n=>{var a;f.each(t.select("br",n),(n=>{t.replace(e.getDoc().createTextNode("\n"),n)})),n.innerHTML=t.encode(null!==(a=n.textContent)&&void 0!==a?a:""),c(e).highlightElement(n),t.setAttrib(n,"data-mce-highlighted",!0),n.className=b(n.className)}))}))})),e.on("PreInit",(()=>{e.parser.addNodeFilter("pre",(e=>{var t;for(let n=0,a=e.length;n{const t=()=>e.execCommand("codesample");e.ui.registry.addToggleButton("codesample",{icon:"code-sample",tooltip:"Insert/edit code sample",onAction:t,onSetup:m(e,(t=>{t.setActive((e=>{const t=e.selection.getStart();return e.dom.is(t,'pre[class*="language-"]')})(e))}))}),e.ui.registry.addMenuItem("codesample",{text:"Code sample...",icon:"code-sample",onAction:t,onSetup:m(e)})})(e),(e=>{e.addCommand("codesample",(()=>{const t=e.selection.getNode();e.selection.isCollapsed()||d(t)?p(e):e.formatter.toggle("code")}))})(e),e.on("dblclick",(t=>{d(t.target)&&p(e)}))}))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/directionality/plugin.min.js b/apps/web-antd/public/tinymce/plugins/directionality/plugin.min.js new file mode 100644 index 0000000..eccdc50 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/directionality/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";var t=tinymce.util.Tools.resolve("tinymce.PluginManager");const e=t=>e=>typeof e===t,o=t=>"string"===(t=>{const e=typeof t;return null===t?"null":"object"===e&&Array.isArray(t)?"array":"object"===e&&(o=r=t,(n=String).prototype.isPrototypeOf(o)||(null===(i=r.constructor)||void 0===i?void 0:i.name)===n.name)?"string":e;var o,r,n,i})(t),r=e("boolean"),n=t=>!(t=>null==t)(t),i=e("function"),s=e("number"),l=(!1,()=>false);class a{constructor(t,e){this.tag=t,this.value=e}static some(t){return new a(!0,t)}static none(){return a.singletonNone}fold(t,e){return this.tag?e(this.value):t()}isSome(){return this.tag}isNone(){return!this.tag}map(t){return this.tag?a.some(t(this.value)):a.none()}bind(t){return this.tag?t(this.value):a.none()}exists(t){return this.tag&&t(this.value)}forall(t){return!this.tag||t(this.value)}filter(t){return!this.tag||t(this.value)?this:a.none()}getOr(t){return this.tag?this.value:t}or(t){return this.tag?this:t}getOrThunk(t){return this.tag?this.value:t()}orThunk(t){return this.tag?this:t()}getOrDie(t){if(this.tag)return this.value;throw new Error(null!=t?t:"Called getOrDie on None")}static from(t){return n(t)?a.some(t):a.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(t){this.tag&&t(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}a.singletonNone=new a(!1);const u=(t,e)=>{for(let o=0,r=t.length;o{if(null==t)throw new Error("Node cannot be null or undefined");return{dom:t}},d=c,h=(t,e)=>{const o=t.dom;if(1!==o.nodeType)return!1;{const t=o;if(void 0!==t.matches)return t.matches(e);if(void 0!==t.msMatchesSelector)return t.msMatchesSelector(e);if(void 0!==t.webkitMatchesSelector)return t.webkitMatchesSelector(e);if(void 0!==t.mozMatchesSelector)return t.mozMatchesSelector(e);throw new Error("Browser lacks native selectors")}};"undefined"!=typeof window?window:Function("return this;")();const m=t=>e=>(t=>t.dom.nodeType)(e)===t,g=m(1),f=m(3),v=m(9),y=m(11),p=(t,e)=>{t.dom.removeAttribute(e)},w=i(Element.prototype.attachShadow)&&i(Node.prototype.getRootNode)?t=>d(t.dom.getRootNode()):t=>v(t)?t:d(t.dom.ownerDocument),b=t=>d(t.dom.host),N=t=>{const e=f(t)?t.dom.parentNode:t.dom;if(null==e||null===e.ownerDocument)return!1;const o=e.ownerDocument;return(t=>{const e=w(t);return y(o=e)&&n(o.dom.host)?a.some(e):a.none();var o})(d(e)).fold((()=>o.body.contains(e)),(r=N,i=b,t=>r(i(t))));var r,i},S=t=>"rtl"===((t,e)=>{const o=t.dom,r=window.getComputedStyle(o).getPropertyValue(e);return""!==r||N(t)?r:((t,e)=>(t=>void 0!==t.style&&i(t.style.getPropertyValue))(t)?t.style.getPropertyValue(e):"")(o,e)})(t,"direction")?"rtl":"ltr",A=(t,e)=>((t,o)=>((t,e)=>{const o=[];for(let r=0,n=t.length;r{const o=t.length,r=new Array(o);for(let n=0;nh(t,e))))(t),E=("li",t=>g(t)&&"li"===t.dom.nodeName.toLowerCase());const T=(t,e,n)=>{u(e,(e=>{const c=d(e),m=E(c),f=((t,e)=>{return(e?(o=t,r="ol,ul",((t,e,o)=>{let n=t.dom;const s=i(o)?o:l;for(;n.parentNode;){n=n.parentNode;const t=d(n);if(h(t,r))return a.some(t);if(s(t))break}return a.none()})(o,0,n)):a.some(t)).getOr(t);var o,r,n})(c,m);var v;(v=f,(t=>a.from(t.dom.parentNode).map(d))(v).filter(g)).each((e=>{if(t.setStyle(f.dom,"direction",null),S(e)===n?p(f,"dir"):((t,e,n)=>{((t,e,n)=>{if(!(o(n)||r(n)||s(n)))throw console.error("Invalid call to Attribute.set. Key ",e,":: Value ",n,":: Element ",t),new Error("Attribute value was not simple");t.setAttribute(e,n+"")})(t.dom,e,n)})(f,"dir",n),S(f)!==n&&t.setStyle(f.dom,"direction",n),m){const e=A(f,"li[dir],li[style]");u(e,(e=>{p(e,"dir"),t.setStyle(e.dom,"direction",null)}))}}))}))},C=(t,e)=>{t.selection.isEditable()&&(T(t.dom,t.selection.getSelectedBlocks(),e),t.nodeChanged())},D=(t,e)=>o=>{const r=r=>{const n=d(r.element);o.setActive(S(n)===e),o.setEnabled(t.selection.isEditable())};return t.on("NodeChange",r),o.setEnabled(t.selection.isEditable()),()=>t.off("NodeChange",r)};t.add("directionality",(t=>{(t=>{t.addCommand("mceDirectionLTR",(()=>{C(t,"ltr")})),t.addCommand("mceDirectionRTL",(()=>{C(t,"rtl")}))})(t),(t=>{t.ui.registry.addToggleButton("ltr",{tooltip:"Left to right",icon:"ltr",onAction:()=>t.execCommand("mceDirectionLTR"),onSetup:D(t,"ltr")}),t.ui.registry.addToggleButton("rtl",{tooltip:"Right to left",icon:"rtl",onAction:()=>t.execCommand("mceDirectionRTL"),onSetup:D(t,"rtl")})})(t)}))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/emoticons/js/emojiimages.js b/apps/web-antd/public/tinymce/plugins/emoticons/js/emojiimages.js new file mode 100644 index 0000000..6fcec71 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/emoticons/js/emojiimages.js @@ -0,0 +1 @@ +window.tinymce.Resource.add("tinymce.plugins.emoticons",{100:{keywords:["score","perfect","numbers","century","exam","quiz","test","pass","hundred"],char:'💯',fitzpatrick_scale:false,category:"symbols"},1234:{keywords:["numbers","blue-square"],char:'🔢',fitzpatrick_scale:false,category:"symbols"},grinning:{keywords:["face","smile","happy","joy",":D","grin"],char:'😀',fitzpatrick_scale:false,category:"people"},grimacing:{keywords:["face","grimace","teeth"],char:'😬',fitzpatrick_scale:false,category:"people"},grin:{keywords:["face","happy","smile","joy","kawaii"],char:'😁',fitzpatrick_scale:false,category:"people"},joy:{keywords:["face","cry","tears","weep","happy","happytears","haha"],char:'😂',fitzpatrick_scale:false,category:"people"},rofl:{keywords:["face","rolling","floor","laughing","lol","haha"],char:'🤣',fitzpatrick_scale:false,category:"people"},partying:{keywords:["face","celebration","woohoo"],char:'🥳',fitzpatrick_scale:false,category:"people"},smiley:{keywords:["face","happy","joy","haha",":D",":)","smile","funny"],char:'😃',fitzpatrick_scale:false,category:"people"},smile:{keywords:["face","happy","joy","funny","haha","laugh","like",":D",":)"],char:'😄',fitzpatrick_scale:false,category:"people"},sweat_smile:{keywords:["face","hot","happy","laugh","sweat","smile","relief"],char:'😅',fitzpatrick_scale:false,category:"people"},laughing:{keywords:["happy","joy","lol","satisfied","haha","face","glad","XD","laugh"],char:'😆',fitzpatrick_scale:false,category:"people"},innocent:{keywords:["face","angel","heaven","halo"],char:'😇',fitzpatrick_scale:false,category:"people"},wink:{keywords:["face","happy","mischievous","secret",";)","smile","eye"],char:'😉',fitzpatrick_scale:false,category:"people"},blush:{keywords:["face","smile","happy","flushed","crush","embarrassed","shy","joy"],char:'😊',fitzpatrick_scale:false,category:"people"},slightly_smiling_face:{keywords:["face","smile"],char:'🙂',fitzpatrick_scale:false,category:"people"},upside_down_face:{keywords:["face","flipped","silly","smile"],char:'🙃',fitzpatrick_scale:false,category:"people"},relaxed:{keywords:["face","blush","massage","happiness"],char:'☺️',fitzpatrick_scale:false,category:"people"},yum:{keywords:["happy","joy","tongue","smile","face","silly","yummy","nom","delicious","savouring"],char:'😋',fitzpatrick_scale:false,category:"people"},relieved:{keywords:["face","relaxed","phew","massage","happiness"],char:'😌',fitzpatrick_scale:false,category:"people"},heart_eyes:{keywords:["face","love","like","affection","valentines","infatuation","crush","heart"],char:'😍',fitzpatrick_scale:false,category:"people"},smiling_face_with_three_hearts:{keywords:["face","love","like","affection","valentines","infatuation","crush","hearts","adore"],char:'🥰',fitzpatrick_scale:false,category:"people"},kissing_heart:{keywords:["face","love","like","affection","valentines","infatuation","kiss"],char:'😘',fitzpatrick_scale:false,category:"people"},kissing:{keywords:["love","like","face","3","valentines","infatuation","kiss"],char:'😗',fitzpatrick_scale:false,category:"people"},kissing_smiling_eyes:{keywords:["face","affection","valentines","infatuation","kiss"],char:'😙',fitzpatrick_scale:false,category:"people"},kissing_closed_eyes:{keywords:["face","love","like","affection","valentines","infatuation","kiss"],char:'😚',fitzpatrick_scale:false,category:"people"},stuck_out_tongue_winking_eye:{keywords:["face","prank","childish","playful","mischievous","smile","wink","tongue"],char:'😜',fitzpatrick_scale:false,category:"people"},zany:{keywords:["face","goofy","crazy"],char:'🤪',fitzpatrick_scale:false,category:"people"},raised_eyebrow:{keywords:["face","distrust","scepticism","disapproval","disbelief","surprise"],char:'🤨',fitzpatrick_scale:false,category:"people"},monocle:{keywords:["face","stuffy","wealthy"],char:'🧐',fitzpatrick_scale:false,category:"people"},stuck_out_tongue_closed_eyes:{keywords:["face","prank","playful","mischievous","smile","tongue"],char:'😝',fitzpatrick_scale:false,category:"people"},stuck_out_tongue:{keywords:["face","prank","childish","playful","mischievous","smile","tongue"],char:'😛',fitzpatrick_scale:false,category:"people"},money_mouth_face:{keywords:["face","rich","dollar","money"],char:'🤑',fitzpatrick_scale:false,category:"people"},nerd_face:{keywords:["face","nerdy","geek","dork"],char:'🤓',fitzpatrick_scale:false,category:"people"},sunglasses:{keywords:["face","cool","smile","summer","beach","sunglass"],char:'😎',fitzpatrick_scale:false,category:"people"},star_struck:{keywords:["face","smile","starry","eyes","grinning"],char:'🤩',fitzpatrick_scale:false,category:"people"},clown_face:{keywords:["face"],char:'🤡',fitzpatrick_scale:false,category:"people"},cowboy_hat_face:{keywords:["face","cowgirl","hat"],char:'🤠',fitzpatrick_scale:false,category:"people"},hugs:{keywords:["face","smile","hug"],char:'🤗',fitzpatrick_scale:false,category:"people"},smirk:{keywords:["face","smile","mean","prank","smug","sarcasm"],char:'😏',fitzpatrick_scale:false,category:"people"},no_mouth:{keywords:["face","hellokitty"],char:'😶',fitzpatrick_scale:false,category:"people"},neutral_face:{keywords:["indifference","meh",":|","neutral"],char:'😐',fitzpatrick_scale:false,category:"people"},expressionless:{keywords:["face","indifferent","-_-","meh","deadpan"],char:'😑',fitzpatrick_scale:false,category:"people"},unamused:{keywords:["indifference","bored","straight face","serious","sarcasm","unimpressed","skeptical","dubious","side_eye"],char:'😒',fitzpatrick_scale:false,category:"people"},roll_eyes:{keywords:["face","eyeroll","frustrated"],char:'🙄',fitzpatrick_scale:false,category:"people"},thinking:{keywords:["face","hmmm","think","consider"],char:'🤔',fitzpatrick_scale:false,category:"people"},lying_face:{keywords:["face","lie","pinocchio"],char:'🤥',fitzpatrick_scale:false,category:"people"},hand_over_mouth:{keywords:["face","whoops","shock","surprise"],char:'🤭',fitzpatrick_scale:false,category:"people"},shushing:{keywords:["face","quiet","shhh"],char:'🤫',fitzpatrick_scale:false,category:"people"},symbols_over_mouth:{keywords:["face","swearing","cursing","cussing","profanity","expletive"],char:'🤬',fitzpatrick_scale:false,category:"people"},exploding_head:{keywords:["face","shocked","mind","blown"],char:'🤯',fitzpatrick_scale:false,category:"people"},flushed:{keywords:["face","blush","shy","flattered"],char:'😳',fitzpatrick_scale:false,category:"people"},disappointed:{keywords:["face","sad","upset","depressed",":("],char:'😞',fitzpatrick_scale:false,category:"people"},worried:{keywords:["face","concern","nervous",":("],char:'😟',fitzpatrick_scale:false,category:"people"},angry:{keywords:["mad","face","annoyed","frustrated"],char:'😠',fitzpatrick_scale:false,category:"people"},rage:{keywords:["angry","mad","hate","despise"],char:'😡',fitzpatrick_scale:false,category:"people"},pensive:{keywords:["face","sad","depressed","upset"],char:'😔',fitzpatrick_scale:false,category:"people"},confused:{keywords:["face","indifference","huh","weird","hmmm",":/"],char:'😕',fitzpatrick_scale:false,category:"people"},slightly_frowning_face:{keywords:["face","frowning","disappointed","sad","upset"],char:'🙁',fitzpatrick_scale:false,category:"people"},frowning_face:{keywords:["face","sad","upset","frown"],char:'☹',fitzpatrick_scale:false,category:"people"},persevere:{keywords:["face","sick","no","upset","oops"],char:'😣',fitzpatrick_scale:false,category:"people"},confounded:{keywords:["face","confused","sick","unwell","oops",":S"],char:'😖',fitzpatrick_scale:false,category:"people"},tired_face:{keywords:["sick","whine","upset","frustrated"],char:'😫',fitzpatrick_scale:false,category:"people"},weary:{keywords:["face","tired","sleepy","sad","frustrated","upset"],char:'😩',fitzpatrick_scale:false,category:"people"},pleading:{keywords:["face","begging","mercy"],char:'🥺',fitzpatrick_scale:false,category:"people"},triumph:{keywords:["face","gas","phew","proud","pride"],char:'😤',fitzpatrick_scale:false,category:"people"},open_mouth:{keywords:["face","surprise","impressed","wow","whoa",":O"],char:'😮',fitzpatrick_scale:false,category:"people"},scream:{keywords:["face","munch","scared","omg"],char:'😱',fitzpatrick_scale:false,category:"people"},fearful:{keywords:["face","scared","terrified","nervous","oops","huh"],char:'😨',fitzpatrick_scale:false,category:"people"},cold_sweat:{keywords:["face","nervous","sweat"],char:'😰',fitzpatrick_scale:false,category:"people"},hushed:{keywords:["face","woo","shh"],char:'😯',fitzpatrick_scale:false,category:"people"},frowning:{keywords:["face","aw","what"],char:'😦',fitzpatrick_scale:false,category:"people"},anguished:{keywords:["face","stunned","nervous"],char:'😧',fitzpatrick_scale:false,category:"people"},cry:{keywords:["face","tears","sad","depressed","upset",":'("],char:'😢',fitzpatrick_scale:false,category:"people"},disappointed_relieved:{keywords:["face","phew","sweat","nervous"],char:'😥',fitzpatrick_scale:false,category:"people"},drooling_face:{keywords:["face"],char:'🤤',fitzpatrick_scale:false,category:"people"},sleepy:{keywords:["face","tired","rest","nap"],char:'😪',fitzpatrick_scale:false,category:"people"},sweat:{keywords:["face","hot","sad","tired","exercise"],char:'😓',fitzpatrick_scale:false,category:"people"},hot:{keywords:["face","feverish","heat","red","sweating"],char:'🥵',fitzpatrick_scale:false,category:"people"},cold:{keywords:["face","blue","freezing","frozen","frostbite","icicles"],char:'🥶',fitzpatrick_scale:false,category:"people"},sob:{keywords:["face","cry","tears","sad","upset","depressed"],char:'😭',fitzpatrick_scale:false,category:"people"},dizzy_face:{keywords:["spent","unconscious","xox","dizzy"],char:'😵',fitzpatrick_scale:false,category:"people"},astonished:{keywords:["face","xox","surprised","poisoned"],char:'😲',fitzpatrick_scale:false,category:"people"},zipper_mouth_face:{keywords:["face","sealed","zipper","secret"],char:'🤐',fitzpatrick_scale:false,category:"people"},nauseated_face:{keywords:["face","vomit","gross","green","sick","throw up","ill"],char:'🤢',fitzpatrick_scale:false,category:"people"},sneezing_face:{keywords:["face","gesundheit","sneeze","sick","allergy"],char:'🤧',fitzpatrick_scale:false,category:"people"},vomiting:{keywords:["face","sick"],char:'🤮',fitzpatrick_scale:false,category:"people"},mask:{keywords:["face","sick","ill","disease"],char:'😷',fitzpatrick_scale:false,category:"people"},face_with_thermometer:{keywords:["sick","temperature","thermometer","cold","fever"],char:'🤒',fitzpatrick_scale:false,category:"people"},face_with_head_bandage:{keywords:["injured","clumsy","bandage","hurt"],char:'🤕',fitzpatrick_scale:false,category:"people"},woozy:{keywords:["face","dizzy","intoxicated","tipsy","wavy"],char:'🥴',fitzpatrick_scale:false,category:"people"},sleeping:{keywords:["face","tired","sleepy","night","zzz"],char:'😴',fitzpatrick_scale:false,category:"people"},zzz:{keywords:["sleepy","tired","dream"],char:'💤',fitzpatrick_scale:false,category:"people"},poop:{keywords:["hankey","shitface","fail","turd","shit"],char:'💩',fitzpatrick_scale:false,category:"people"},smiling_imp:{keywords:["devil","horns"],char:'😈',fitzpatrick_scale:false,category:"people"},imp:{keywords:["devil","angry","horns"],char:'👿',fitzpatrick_scale:false,category:"people"},japanese_ogre:{keywords:["monster","red","mask","halloween","scary","creepy","devil","demon","japanese","ogre"],char:'👹',fitzpatrick_scale:false,category:"people"},japanese_goblin:{keywords:["red","evil","mask","monster","scary","creepy","japanese","goblin"],char:'👺',fitzpatrick_scale:false,category:"people"},skull:{keywords:["dead","skeleton","creepy","death"],char:'💀',fitzpatrick_scale:false,category:"people"},ghost:{keywords:["halloween","spooky","scary"],char:'👻',fitzpatrick_scale:false,category:"people"},alien:{keywords:["UFO","paul","weird","outer_space"],char:'👽',fitzpatrick_scale:false,category:"people"},robot:{keywords:["computer","machine","bot"],char:'🤖',fitzpatrick_scale:false,category:"people"},smiley_cat:{keywords:["animal","cats","happy","smile"],char:'😺',fitzpatrick_scale:false,category:"people"},smile_cat:{keywords:["animal","cats","smile"],char:'😸',fitzpatrick_scale:false,category:"people"},joy_cat:{keywords:["animal","cats","haha","happy","tears"],char:'😹',fitzpatrick_scale:false,category:"people"},heart_eyes_cat:{keywords:["animal","love","like","affection","cats","valentines","heart"],char:'😻',fitzpatrick_scale:false,category:"people"},smirk_cat:{keywords:["animal","cats","smirk"],char:'😼',fitzpatrick_scale:false,category:"people"},kissing_cat:{keywords:["animal","cats","kiss"],char:'😽',fitzpatrick_scale:false,category:"people"},scream_cat:{keywords:["animal","cats","munch","scared","scream"],char:'🙀',fitzpatrick_scale:false,category:"people"},crying_cat_face:{keywords:["animal","tears","weep","sad","cats","upset","cry"],char:'😿',fitzpatrick_scale:false,category:"people"},pouting_cat:{keywords:["animal","cats"],char:'😾',fitzpatrick_scale:false,category:"people"},palms_up:{keywords:["hands","gesture","cupped","prayer"],char:'🤲',fitzpatrick_scale:true,category:"people"},raised_hands:{keywords:["gesture","hooray","yea","celebration","hands"],char:'🙌',fitzpatrick_scale:true,category:"people"},clap:{keywords:["hands","praise","applause","congrats","yay"],char:'👏',fitzpatrick_scale:true,category:"people"},wave:{keywords:["hands","gesture","goodbye","solong","farewell","hello","hi","palm"],char:'👋',fitzpatrick_scale:true,category:"people"},call_me_hand:{keywords:["hands","gesture"],char:'🤙',fitzpatrick_scale:true,category:"people"},"+1":{keywords:["thumbsup","yes","awesome","good","agree","accept","cool","hand","like"],char:'👍',fitzpatrick_scale:true,category:"people"},"-1":{keywords:["thumbsdown","no","dislike","hand"],char:'👎',fitzpatrick_scale:true,category:"people"},facepunch:{keywords:["angry","violence","fist","hit","attack","hand"],char:'👊',fitzpatrick_scale:true,category:"people"},fist:{keywords:["fingers","hand","grasp"],char:'✊',fitzpatrick_scale:true,category:"people"},fist_left:{keywords:["hand","fistbump"],char:'🤛',fitzpatrick_scale:true,category:"people"},fist_right:{keywords:["hand","fistbump"],char:'🤜',fitzpatrick_scale:true,category:"people"},v:{keywords:["fingers","ohyeah","hand","peace","victory","two"],char:'✌',fitzpatrick_scale:true,category:"people"},ok_hand:{keywords:["fingers","limbs","perfect","ok","okay"],char:'👌',fitzpatrick_scale:true,category:"people"},raised_hand:{keywords:["fingers","stop","highfive","palm","ban"],char:'✋',fitzpatrick_scale:true,category:"people"},raised_back_of_hand:{keywords:["fingers","raised","backhand"],char:'🤚',fitzpatrick_scale:true,category:"people"},open_hands:{keywords:["fingers","butterfly","hands","open"],char:'👐',fitzpatrick_scale:true,category:"people"},muscle:{keywords:["arm","flex","hand","summer","strong","biceps"],char:'💪',fitzpatrick_scale:true,category:"people"},pray:{keywords:["please","hope","wish","namaste","highfive"],char:'🙏',fitzpatrick_scale:true,category:"people"},foot:{keywords:["kick","stomp"],char:'🦶',fitzpatrick_scale:true,category:"people"},leg:{keywords:["kick","limb"],char:'🦵',fitzpatrick_scale:true,category:"people"},handshake:{keywords:["agreement","shake"],char:'🤝',fitzpatrick_scale:false,category:"people"},point_up:{keywords:["hand","fingers","direction","up"],char:'☝',fitzpatrick_scale:true,category:"people"},point_up_2:{keywords:["fingers","hand","direction","up"],char:'👆',fitzpatrick_scale:true,category:"people"},point_down:{keywords:["fingers","hand","direction","down"],char:'👇',fitzpatrick_scale:true,category:"people"},point_left:{keywords:["direction","fingers","hand","left"],char:'👈',fitzpatrick_scale:true,category:"people"},point_right:{keywords:["fingers","hand","direction","right"],char:'👉',fitzpatrick_scale:true,category:"people"},fu:{keywords:["hand","fingers","rude","middle","flipping"],char:'🖕',fitzpatrick_scale:true,category:"people"},raised_hand_with_fingers_splayed:{keywords:["hand","fingers","palm"],char:'🖐',fitzpatrick_scale:true,category:"people"},love_you:{keywords:["hand","fingers","gesture"],char:'🤟',fitzpatrick_scale:true,category:"people"},metal:{keywords:["hand","fingers","evil_eye","sign_of_horns","rock_on"],char:'🤘',fitzpatrick_scale:true,category:"people"},crossed_fingers:{keywords:["good","lucky"],char:'🤞',fitzpatrick_scale:true,category:"people"},vulcan_salute:{keywords:["hand","fingers","spock","star trek"],char:'🖖',fitzpatrick_scale:true,category:"people"},writing_hand:{keywords:["lower_left_ballpoint_pen","stationery","write","compose"],char:'✍',fitzpatrick_scale:true,category:"people"},selfie:{keywords:["camera","phone"],char:'🤳',fitzpatrick_scale:true,category:"people"},nail_care:{keywords:["beauty","manicure","finger","fashion","nail"],char:'💅',fitzpatrick_scale:true,category:"people"},lips:{keywords:["mouth","kiss"],char:'👄',fitzpatrick_scale:false,category:"people"},tooth:{keywords:["teeth","dentist"],char:'🦷',fitzpatrick_scale:false,category:"people"},tongue:{keywords:["mouth","playful"],char:'👅',fitzpatrick_scale:false,category:"people"},ear:{keywords:["face","hear","sound","listen"],char:'👂',fitzpatrick_scale:true,category:"people"},nose:{keywords:["smell","sniff"],char:'👃',fitzpatrick_scale:true,category:"people"},eye:{keywords:["face","look","see","watch","stare"],char:'👁',fitzpatrick_scale:false,category:"people"},eyes:{keywords:["look","watch","stalk","peek","see"],char:'👀',fitzpatrick_scale:false,category:"people"},brain:{keywords:["smart","intelligent"],char:'🧠',fitzpatrick_scale:false,category:"people"},bust_in_silhouette:{keywords:["user","person","human"],char:'👤',fitzpatrick_scale:false,category:"people"},busts_in_silhouette:{keywords:["user","person","human","group","team"],char:'👥',fitzpatrick_scale:false,category:"people"},speaking_head:{keywords:["user","person","human","sing","say","talk"],char:'🗣',fitzpatrick_scale:false,category:"people"},baby:{keywords:["child","boy","girl","toddler"],char:'👶',fitzpatrick_scale:true,category:"people"},child:{keywords:["gender-neutral","young"],char:'🧒',fitzpatrick_scale:true,category:"people"},boy:{keywords:["man","male","guy","teenager"],char:'👦',fitzpatrick_scale:true,category:"people"},girl:{keywords:["female","woman","teenager"],char:'👧',fitzpatrick_scale:true,category:"people"},adult:{keywords:["gender-neutral","person"],char:'🧑',fitzpatrick_scale:true,category:"people"},man:{keywords:["mustache","father","dad","guy","classy","sir","moustache"],char:'👨',fitzpatrick_scale:true,category:"people"},woman:{keywords:["female","girls","lady"],char:'👩',fitzpatrick_scale:true,category:"people"},blonde_woman:{keywords:["woman","female","girl","blonde","person"],char:'👱‍♀️',fitzpatrick_scale:true,category:"people"},blonde_man:{keywords:["man","male","boy","blonde","guy","person"],char:'👱',fitzpatrick_scale:true,category:"people"},bearded_person:{keywords:["person","bewhiskered"],char:'🧔',fitzpatrick_scale:true,category:"people"},older_adult:{keywords:["human","elder","senior","gender-neutral"],char:'🧓',fitzpatrick_scale:true,category:"people"},older_man:{keywords:["human","male","men","old","elder","senior"],char:'👴',fitzpatrick_scale:true,category:"people"},older_woman:{keywords:["human","female","women","lady","old","elder","senior"],char:'👵',fitzpatrick_scale:true,category:"people"},man_with_gua_pi_mao:{keywords:["male","boy","chinese"],char:'👲',fitzpatrick_scale:true,category:"people"},woman_with_headscarf:{keywords:["female","hijab","mantilla","tichel"],char:'🧕',fitzpatrick_scale:true,category:"people"},woman_with_turban:{keywords:["female","indian","hinduism","arabs","woman"],char:'👳‍♀️',fitzpatrick_scale:true,category:"people"},man_with_turban:{keywords:["male","indian","hinduism","arabs"],char:'👳',fitzpatrick_scale:true,category:"people"},policewoman:{keywords:["woman","police","law","legal","enforcement","arrest","911","female"],char:'👮‍♀️',fitzpatrick_scale:true,category:"people"},policeman:{keywords:["man","police","law","legal","enforcement","arrest","911"],char:'👮',fitzpatrick_scale:true,category:"people"},construction_worker_woman:{keywords:["female","human","wip","build","construction","worker","labor","woman"],char:'👷‍♀️',fitzpatrick_scale:true,category:"people"},construction_worker_man:{keywords:["male","human","wip","guy","build","construction","worker","labor"],char:'👷',fitzpatrick_scale:true,category:"people"},guardswoman:{keywords:["uk","gb","british","female","royal","woman"],char:'💂‍♀️',fitzpatrick_scale:true,category:"people"},guardsman:{keywords:["uk","gb","british","male","guy","royal"],char:'💂',fitzpatrick_scale:true,category:"people"},female_detective:{keywords:["human","spy","detective","female","woman"],char:'🕵️‍♀️',fitzpatrick_scale:true,category:"people"},male_detective:{keywords:["human","spy","detective"],char:'🕵',fitzpatrick_scale:true,category:"people"},woman_health_worker:{keywords:["doctor","nurse","therapist","healthcare","woman","human"],char:'👩‍⚕️',fitzpatrick_scale:true,category:"people"},man_health_worker:{keywords:["doctor","nurse","therapist","healthcare","man","human"],char:'👨‍⚕️',fitzpatrick_scale:true,category:"people"},woman_farmer:{keywords:["rancher","gardener","woman","human"],char:'👩‍🌾',fitzpatrick_scale:true,category:"people"},man_farmer:{keywords:["rancher","gardener","man","human"],char:'👨‍🌾',fitzpatrick_scale:true,category:"people"},woman_cook:{keywords:["chef","woman","human"],char:'👩‍🍳',fitzpatrick_scale:true,category:"people"},man_cook:{keywords:["chef","man","human"],char:'👨‍🍳',fitzpatrick_scale:true,category:"people"},woman_student:{keywords:["graduate","woman","human"],char:'👩‍🎓',fitzpatrick_scale:true,category:"people"},man_student:{keywords:["graduate","man","human"],char:'👨‍🎓',fitzpatrick_scale:true,category:"people"},woman_singer:{keywords:["rockstar","entertainer","woman","human"],char:'👩‍🎤',fitzpatrick_scale:true,category:"people"},man_singer:{keywords:["rockstar","entertainer","man","human"],char:'👨‍🎤',fitzpatrick_scale:true,category:"people"},woman_teacher:{keywords:["instructor","professor","woman","human"],char:'👩‍🏫',fitzpatrick_scale:true,category:"people"},man_teacher:{keywords:["instructor","professor","man","human"],char:'👨‍🏫',fitzpatrick_scale:true,category:"people"},woman_factory_worker:{keywords:["assembly","industrial","woman","human"],char:'👩‍🏭',fitzpatrick_scale:true,category:"people"},man_factory_worker:{keywords:["assembly","industrial","man","human"],char:'👨‍🏭',fitzpatrick_scale:true,category:"people"},woman_technologist:{keywords:["coder","developer","engineer","programmer","software","woman","human","laptop","computer"],char:'👩‍💻',fitzpatrick_scale:true,category:"people"},man_technologist:{keywords:["coder","developer","engineer","programmer","software","man","human","laptop","computer"],char:'👨‍💻',fitzpatrick_scale:true,category:"people"},woman_office_worker:{keywords:["business","manager","woman","human"],char:'👩‍💼',fitzpatrick_scale:true,category:"people"},man_office_worker:{keywords:["business","manager","man","human"],char:'👨‍💼',fitzpatrick_scale:true,category:"people"},woman_mechanic:{keywords:["plumber","woman","human","wrench"],char:'👩‍🔧',fitzpatrick_scale:true,category:"people"},man_mechanic:{keywords:["plumber","man","human","wrench"],char:'👨‍🔧',fitzpatrick_scale:true,category:"people"},woman_scientist:{keywords:["biologist","chemist","engineer","physicist","woman","human"],char:'👩‍🔬',fitzpatrick_scale:true,category:"people"},man_scientist:{keywords:["biologist","chemist","engineer","physicist","man","human"],char:'👨‍🔬',fitzpatrick_scale:true,category:"people"},woman_artist:{keywords:["painter","woman","human"],char:'👩‍🎨',fitzpatrick_scale:true,category:"people"},man_artist:{keywords:["painter","man","human"],char:'👨‍🎨',fitzpatrick_scale:true,category:"people"},woman_firefighter:{keywords:["fireman","woman","human"],char:'👩‍🚒',fitzpatrick_scale:true,category:"people"},man_firefighter:{keywords:["fireman","man","human"],char:'👨‍🚒',fitzpatrick_scale:true,category:"people"},woman_pilot:{keywords:["aviator","plane","woman","human"],char:'👩‍✈️',fitzpatrick_scale:true,category:"people"},man_pilot:{keywords:["aviator","plane","man","human"],char:'👨‍✈️',fitzpatrick_scale:true,category:"people"},woman_astronaut:{keywords:["space","rocket","woman","human"],char:'👩‍🚀',fitzpatrick_scale:true,category:"people"},man_astronaut:{keywords:["space","rocket","man","human"],char:'👨‍🚀',fitzpatrick_scale:true,category:"people"},woman_judge:{keywords:["justice","court","woman","human"],char:'👩‍⚖️',fitzpatrick_scale:true,category:"people"},man_judge:{keywords:["justice","court","man","human"],char:'👨‍⚖️',fitzpatrick_scale:true,category:"people"},woman_superhero:{keywords:["woman","female","good","heroine","superpowers"],char:'🦸‍♀️',fitzpatrick_scale:true,category:"people"},man_superhero:{keywords:["man","male","good","hero","superpowers"],char:'🦸‍♂️',fitzpatrick_scale:true,category:"people"},woman_supervillain:{keywords:["woman","female","evil","bad","criminal","heroine","superpowers"],char:'🦹‍♀️',fitzpatrick_scale:true,category:"people"},man_supervillain:{keywords:["man","male","evil","bad","criminal","hero","superpowers"],char:'🦹‍♂️',fitzpatrick_scale:true,category:"people"},mrs_claus:{keywords:["woman","female","xmas","mother christmas"],char:'🤶',fitzpatrick_scale:true,category:"people"},santa:{keywords:["festival","man","male","xmas","father christmas"],char:'🎅',fitzpatrick_scale:true,category:"people"},sorceress:{keywords:["woman","female","mage","witch"],char:'🧙‍♀️',fitzpatrick_scale:true,category:"people"},wizard:{keywords:["man","male","mage","sorcerer"],char:'🧙‍♂️',fitzpatrick_scale:true,category:"people"},woman_elf:{keywords:["woman","female"],char:'🧝‍♀️',fitzpatrick_scale:true,category:"people"},man_elf:{keywords:["man","male"],char:'🧝‍♂️',fitzpatrick_scale:true,category:"people"},woman_vampire:{keywords:["woman","female"],char:'🧛‍♀️',fitzpatrick_scale:true,category:"people"},man_vampire:{keywords:["man","male","dracula"],char:'🧛‍♂️',fitzpatrick_scale:true,category:"people"},woman_zombie:{keywords:["woman","female","undead","walking dead"],char:'🧟‍♀️',fitzpatrick_scale:false,category:"people"},man_zombie:{keywords:["man","male","dracula","undead","walking dead"],char:'🧟‍♂️',fitzpatrick_scale:false,category:"people"},woman_genie:{keywords:["woman","female"],char:'🧞‍♀️',fitzpatrick_scale:false,category:"people"},man_genie:{keywords:["man","male"],char:'🧞‍♂️',fitzpatrick_scale:false,category:"people"},mermaid:{keywords:["woman","female","merwoman","ariel"],char:'🧜‍♀️',fitzpatrick_scale:true,category:"people"},merman:{keywords:["man","male","triton"],char:'🧜‍♂️',fitzpatrick_scale:true,category:"people"},woman_fairy:{keywords:["woman","female"],char:'🧚‍♀️',fitzpatrick_scale:true,category:"people"},man_fairy:{keywords:["man","male"],char:'🧚‍♂️',fitzpatrick_scale:true,category:"people"},angel:{keywords:["heaven","wings","halo"],char:'👼',fitzpatrick_scale:true,category:"people"},pregnant_woman:{keywords:["baby"],char:'🤰',fitzpatrick_scale:true,category:"people"},breastfeeding:{keywords:["nursing","baby"],char:'🤱',fitzpatrick_scale:true,category:"people"},princess:{keywords:["girl","woman","female","blond","crown","royal","queen"],char:'👸',fitzpatrick_scale:true,category:"people"},prince:{keywords:["boy","man","male","crown","royal","king"],char:'🤴',fitzpatrick_scale:true,category:"people"},bride_with_veil:{keywords:["couple","marriage","wedding","woman","bride"],char:'👰',fitzpatrick_scale:true,category:"people"},man_in_tuxedo:{keywords:["couple","marriage","wedding","groom"],char:'🤵',fitzpatrick_scale:true,category:"people"},running_woman:{keywords:["woman","walking","exercise","race","running","female"],char:'🏃‍♀️',fitzpatrick_scale:true,category:"people"},running_man:{keywords:["man","walking","exercise","race","running"],char:'🏃',fitzpatrick_scale:true,category:"people"},walking_woman:{keywords:["human","feet","steps","woman","female"],char:'🚶‍♀️',fitzpatrick_scale:true,category:"people"},walking_man:{keywords:["human","feet","steps"],char:'🚶',fitzpatrick_scale:true,category:"people"},dancer:{keywords:["female","girl","woman","fun"],char:'💃',fitzpatrick_scale:true,category:"people"},man_dancing:{keywords:["male","boy","fun","dancer"],char:'🕺',fitzpatrick_scale:true,category:"people"},dancing_women:{keywords:["female","bunny","women","girls"],char:'👯',fitzpatrick_scale:false,category:"people"},dancing_men:{keywords:["male","bunny","men","boys"],char:'👯‍♂️',fitzpatrick_scale:false,category:"people"},couple:{keywords:["pair","people","human","love","date","dating","like","affection","valentines","marriage"],char:'👫',fitzpatrick_scale:false,category:"people"},two_men_holding_hands:{keywords:["pair","couple","love","like","bromance","friendship","people","human"],char:'👬',fitzpatrick_scale:false,category:"people"},two_women_holding_hands:{keywords:["pair","friendship","couple","love","like","female","people","human"],char:'👭',fitzpatrick_scale:false,category:"people"},bowing_woman:{keywords:["woman","female","girl"],char:'🙇‍♀️',fitzpatrick_scale:true,category:"people"},bowing_man:{keywords:["man","male","boy"],char:'🙇',fitzpatrick_scale:true,category:"people"},man_facepalming:{keywords:["man","male","boy","disbelief"],char:'🤦‍♂️',fitzpatrick_scale:true,category:"people"},woman_facepalming:{keywords:["woman","female","girl","disbelief"],char:'🤦‍♀️',fitzpatrick_scale:true,category:"people"},woman_shrugging:{keywords:["woman","female","girl","confused","indifferent","doubt"],char:'🤷',fitzpatrick_scale:true,category:"people"},man_shrugging:{keywords:["man","male","boy","confused","indifferent","doubt"],char:'🤷‍♂️',fitzpatrick_scale:true,category:"people"},tipping_hand_woman:{keywords:["female","girl","woman","human","information"],char:'💁',fitzpatrick_scale:true,category:"people"},tipping_hand_man:{keywords:["male","boy","man","human","information"],char:'💁‍♂️',fitzpatrick_scale:true,category:"people"},no_good_woman:{keywords:["female","girl","woman","nope"],char:'🙅',fitzpatrick_scale:true,category:"people"},no_good_man:{keywords:["male","boy","man","nope"],char:'🙅‍♂️',fitzpatrick_scale:true,category:"people"},ok_woman:{keywords:["women","girl","female","pink","human","woman"],char:'🙆',fitzpatrick_scale:true,category:"people"},ok_man:{keywords:["men","boy","male","blue","human","man"],char:'🙆‍♂️',fitzpatrick_scale:true,category:"people"},raising_hand_woman:{keywords:["female","girl","woman"],char:'🙋',fitzpatrick_scale:true,category:"people"},raising_hand_man:{keywords:["male","boy","man"],char:'🙋‍♂️',fitzpatrick_scale:true,category:"people"},pouting_woman:{keywords:["female","girl","woman"],char:'🙎',fitzpatrick_scale:true,category:"people"},pouting_man:{keywords:["male","boy","man"],char:'🙎‍♂️',fitzpatrick_scale:true,category:"people"},frowning_woman:{keywords:["female","girl","woman","sad","depressed","discouraged","unhappy"],char:'🙍',fitzpatrick_scale:true,category:"people"},frowning_man:{keywords:["male","boy","man","sad","depressed","discouraged","unhappy"],char:'🙍‍♂️',fitzpatrick_scale:true,category:"people"},haircut_woman:{keywords:["female","girl","woman"],char:'💇',fitzpatrick_scale:true,category:"people"},haircut_man:{keywords:["male","boy","man"],char:'💇‍♂️',fitzpatrick_scale:true,category:"people"},massage_woman:{keywords:["female","girl","woman","head"],char:'💆',fitzpatrick_scale:true,category:"people"},massage_man:{keywords:["male","boy","man","head"],char:'💆‍♂️',fitzpatrick_scale:true,category:"people"},woman_in_steamy_room:{keywords:["female","woman","spa","steamroom","sauna"],char:'🧖‍♀️',fitzpatrick_scale:true,category:"people"},man_in_steamy_room:{keywords:["male","man","spa","steamroom","sauna"],char:'🧖‍♂️',fitzpatrick_scale:true,category:"people"},couple_with_heart_woman_man:{keywords:["pair","love","like","affection","human","dating","valentines","marriage"],char:'💑',fitzpatrick_scale:false,category:"people"},couple_with_heart_woman_woman:{keywords:["pair","love","like","affection","human","dating","valentines","marriage"],char:'👩‍❤️‍👩',fitzpatrick_scale:false,category:"people"},couple_with_heart_man_man:{keywords:["pair","love","like","affection","human","dating","valentines","marriage"],char:'👨‍❤️‍👨',fitzpatrick_scale:false,category:"people"},couplekiss_man_woman:{keywords:["pair","valentines","love","like","dating","marriage"],char:'💏',fitzpatrick_scale:false,category:"people"},couplekiss_woman_woman:{keywords:["pair","valentines","love","like","dating","marriage"],char:'👩‍❤️‍💋‍👩',fitzpatrick_scale:false,category:"people"},couplekiss_man_man:{keywords:["pair","valentines","love","like","dating","marriage"],char:'👨‍❤️‍💋‍👨',fitzpatrick_scale:false,category:"people"},family_man_woman_boy:{keywords:["home","parents","child","mom","dad","father","mother","people","human"],char:'👪',fitzpatrick_scale:false,category:"people"},family_man_woman_girl:{keywords:["home","parents","people","human","child"],char:'👨‍👩‍👧',fitzpatrick_scale:false,category:"people"},family_man_woman_girl_boy:{keywords:["home","parents","people","human","children"],char:'👨‍👩‍👧‍👦',fitzpatrick_scale:false,category:"people"},family_man_woman_boy_boy:{keywords:["home","parents","people","human","children"],char:'👨‍👩‍👦‍👦',fitzpatrick_scale:false,category:"people"},family_man_woman_girl_girl:{keywords:["home","parents","people","human","children"],char:'👨‍👩‍👧‍👧',fitzpatrick_scale:false,category:"people"},family_woman_woman_boy:{keywords:["home","parents","people","human","children"],char:'👩‍👩‍👦',fitzpatrick_scale:false,category:"people"},family_woman_woman_girl:{keywords:["home","parents","people","human","children"],char:'👩‍👩‍👧',fitzpatrick_scale:false,category:"people"},family_woman_woman_girl_boy:{keywords:["home","parents","people","human","children"],char:'👩‍👩‍👧‍👦',fitzpatrick_scale:false,category:"people"},family_woman_woman_boy_boy:{keywords:["home","parents","people","human","children"],char:'👩‍👩‍👦‍👦',fitzpatrick_scale:false,category:"people"},family_woman_woman_girl_girl:{keywords:["home","parents","people","human","children"],char:'👩‍👩‍👧‍👧',fitzpatrick_scale:false,category:"people"},family_man_man_boy:{keywords:["home","parents","people","human","children"],char:'👨‍👨‍👦',fitzpatrick_scale:false,category:"people"},family_man_man_girl:{keywords:["home","parents","people","human","children"],char:'👨‍👨‍👧',fitzpatrick_scale:false,category:"people"},family_man_man_girl_boy:{keywords:["home","parents","people","human","children"],char:'👨‍👨‍👧‍👦',fitzpatrick_scale:false,category:"people"},family_man_man_boy_boy:{keywords:["home","parents","people","human","children"],char:'👨‍👨‍👦‍👦',fitzpatrick_scale:false,category:"people"},family_man_man_girl_girl:{keywords:["home","parents","people","human","children"],char:'👨‍👨‍👧‍👧',fitzpatrick_scale:false,category:"people"},family_woman_boy:{keywords:["home","parent","people","human","child"],char:'👩‍👦',fitzpatrick_scale:false,category:"people"},family_woman_girl:{keywords:["home","parent","people","human","child"],char:'👩‍👧',fitzpatrick_scale:false,category:"people"},family_woman_girl_boy:{keywords:["home","parent","people","human","children"],char:'👩‍👧‍👦',fitzpatrick_scale:false,category:"people"},family_woman_boy_boy:{keywords:["home","parent","people","human","children"],char:'👩‍👦‍👦',fitzpatrick_scale:false,category:"people"},family_woman_girl_girl:{keywords:["home","parent","people","human","children"],char:'👩‍👧‍👧',fitzpatrick_scale:false,category:"people"},family_man_boy:{keywords:["home","parent","people","human","child"],char:'👨‍👦',fitzpatrick_scale:false,category:"people"},family_man_girl:{keywords:["home","parent","people","human","child"],char:'👨‍👧',fitzpatrick_scale:false,category:"people"},family_man_girl_boy:{keywords:["home","parent","people","human","children"],char:'👨‍👧‍👦',fitzpatrick_scale:false,category:"people"},family_man_boy_boy:{keywords:["home","parent","people","human","children"],char:'👨‍👦‍👦',fitzpatrick_scale:false,category:"people"},family_man_girl_girl:{keywords:["home","parent","people","human","children"],char:'👨‍👧‍👧',fitzpatrick_scale:false,category:"people"},yarn:{keywords:["ball","crochet","knit"],char:'🧶',fitzpatrick_scale:false,category:"people"},thread:{keywords:["needle","sewing","spool","string"],char:'🧵',fitzpatrick_scale:false,category:"people"},coat:{keywords:["jacket"],char:'🧥',fitzpatrick_scale:false,category:"people"},labcoat:{keywords:["doctor","experiment","scientist","chemist"],char:'🥼',fitzpatrick_scale:false,category:"people"},womans_clothes:{keywords:["fashion","shopping_bags","female"],char:'👚',fitzpatrick_scale:false,category:"people"},tshirt:{keywords:["fashion","cloth","casual","shirt","tee"],char:'👕',fitzpatrick_scale:false,category:"people"},jeans:{keywords:["fashion","shopping"],char:'👖',fitzpatrick_scale:false,category:"people"},necktie:{keywords:["shirt","suitup","formal","fashion","cloth","business"],char:'👔',fitzpatrick_scale:false,category:"people"},dress:{keywords:["clothes","fashion","shopping"],char:'👗',fitzpatrick_scale:false,category:"people"},bikini:{keywords:["swimming","female","woman","girl","fashion","beach","summer"],char:'👙',fitzpatrick_scale:false,category:"people"},kimono:{keywords:["dress","fashion","women","female","japanese"],char:'👘',fitzpatrick_scale:false,category:"people"},lipstick:{keywords:["female","girl","fashion","woman"],char:'💄',fitzpatrick_scale:false,category:"people"},kiss:{keywords:["face","lips","love","like","affection","valentines"],char:'💋',fitzpatrick_scale:false,category:"people"},footprints:{keywords:["feet","tracking","walking","beach"],char:'👣',fitzpatrick_scale:false,category:"people"},flat_shoe:{keywords:["ballet","slip-on","slipper"],char:'🥿',fitzpatrick_scale:false,category:"people"},high_heel:{keywords:["fashion","shoes","female","pumps","stiletto"],char:'👠',fitzpatrick_scale:false,category:"people"},sandal:{keywords:["shoes","fashion","flip flops"],char:'👡',fitzpatrick_scale:false,category:"people"},boot:{keywords:["shoes","fashion"],char:'👢',fitzpatrick_scale:false,category:"people"},mans_shoe:{keywords:["fashion","male"],char:'👞',fitzpatrick_scale:false,category:"people"},athletic_shoe:{keywords:["shoes","sports","sneakers"],char:'👟',fitzpatrick_scale:false,category:"people"},hiking_boot:{keywords:["backpacking","camping","hiking"],char:'🥾',fitzpatrick_scale:false,category:"people"},socks:{keywords:["stockings","clothes"],char:'🧦',fitzpatrick_scale:false,category:"people"},gloves:{keywords:["hands","winter","clothes"],char:'🧤',fitzpatrick_scale:false,category:"people"},scarf:{keywords:["neck","winter","clothes"],char:'🧣',fitzpatrick_scale:false,category:"people"},womans_hat:{keywords:["fashion","accessories","female","lady","spring"],char:'👒',fitzpatrick_scale:false,category:"people"},tophat:{keywords:["magic","gentleman","classy","circus"],char:'🎩',fitzpatrick_scale:false,category:"people"},billed_hat:{keywords:["cap","baseball"],char:'🧢',fitzpatrick_scale:false,category:"people"},rescue_worker_helmet:{keywords:["construction","build"],char:'⛑',fitzpatrick_scale:false,category:"people"},mortar_board:{keywords:["school","college","degree","university","graduation","cap","hat","legal","learn","education"],char:'🎓',fitzpatrick_scale:false,category:"people"},crown:{keywords:["king","kod","leader","royalty","lord"],char:'👑',fitzpatrick_scale:false,category:"people"},school_satchel:{keywords:["student","education","bag","backpack"],char:'🎒',fitzpatrick_scale:false,category:"people"},luggage:{keywords:["packing","travel"],char:'🧳',fitzpatrick_scale:false,category:"people"},pouch:{keywords:["bag","accessories","shopping"],char:'👝',fitzpatrick_scale:false,category:"people"},purse:{keywords:["fashion","accessories","money","sales","shopping"],char:'👛',fitzpatrick_scale:false,category:"people"},handbag:{keywords:["fashion","accessory","accessories","shopping"],char:'👜',fitzpatrick_scale:false,category:"people"},briefcase:{keywords:["business","documents","work","law","legal","job","career"],char:'💼',fitzpatrick_scale:false,category:"people"},eyeglasses:{keywords:["fashion","accessories","eyesight","nerdy","dork","geek"],char:'👓',fitzpatrick_scale:false,category:"people"},dark_sunglasses:{keywords:["face","cool","accessories"],char:'🕶',fitzpatrick_scale:false,category:"people"},goggles:{keywords:["eyes","protection","safety"],char:'🥽',fitzpatrick_scale:false,category:"people"},ring:{keywords:["wedding","propose","marriage","valentines","diamond","fashion","jewelry","gem","engagement"],char:'💍',fitzpatrick_scale:false,category:"people"},closed_umbrella:{keywords:["weather","rain","drizzle"],char:'🌂',fitzpatrick_scale:false,category:"people"},dog:{keywords:["animal","friend","nature","woof","puppy","pet","faithful"],char:'🐶',fitzpatrick_scale:false,category:"animals_and_nature"},cat:{keywords:["animal","meow","nature","pet","kitten"],char:'🐱',fitzpatrick_scale:false,category:"animals_and_nature"},mouse:{keywords:["animal","nature","cheese_wedge","rodent"],char:'🐭',fitzpatrick_scale:false,category:"animals_and_nature"},hamster:{keywords:["animal","nature"],char:'🐹',fitzpatrick_scale:false,category:"animals_and_nature"},rabbit:{keywords:["animal","nature","pet","spring","magic","bunny"],char:'🐰',fitzpatrick_scale:false,category:"animals_and_nature"},fox_face:{keywords:["animal","nature","face"],char:'🦊',fitzpatrick_scale:false,category:"animals_and_nature"},bear:{keywords:["animal","nature","wild"],char:'🐻',fitzpatrick_scale:false,category:"animals_and_nature"},panda_face:{keywords:["animal","nature","panda"],char:'🐼',fitzpatrick_scale:false,category:"animals_and_nature"},koala:{keywords:["animal","nature"],char:'🐨',fitzpatrick_scale:false,category:"animals_and_nature"},tiger:{keywords:["animal","cat","danger","wild","nature","roar"],char:'🐯',fitzpatrick_scale:false,category:"animals_and_nature"},lion:{keywords:["animal","nature"],char:'🦁',fitzpatrick_scale:false,category:"animals_and_nature"},cow:{keywords:["beef","ox","animal","nature","moo","milk"],char:'🐮',fitzpatrick_scale:false,category:"animals_and_nature"},pig:{keywords:["animal","oink","nature"],char:'🐷',fitzpatrick_scale:false,category:"animals_and_nature"},pig_nose:{keywords:["animal","oink"],char:'🐽',fitzpatrick_scale:false,category:"animals_and_nature"},frog:{keywords:["animal","nature","croak","toad"],char:'🐸',fitzpatrick_scale:false,category:"animals_and_nature"},squid:{keywords:["animal","nature","ocean","sea"],char:'🦑',fitzpatrick_scale:false,category:"animals_and_nature"},octopus:{keywords:["animal","creature","ocean","sea","nature","beach"],char:'🐙',fitzpatrick_scale:false,category:"animals_and_nature"},shrimp:{keywords:["animal","ocean","nature","seafood"],char:'🦐',fitzpatrick_scale:false,category:"animals_and_nature"},monkey_face:{keywords:["animal","nature","circus"],char:'🐵',fitzpatrick_scale:false,category:"animals_and_nature"},gorilla:{keywords:["animal","nature","circus"],char:'🦍',fitzpatrick_scale:false,category:"animals_and_nature"},see_no_evil:{keywords:["monkey","animal","nature","haha"],char:'🙈',fitzpatrick_scale:false,category:"animals_and_nature"},hear_no_evil:{keywords:["animal","monkey","nature"],char:'🙉',fitzpatrick_scale:false,category:"animals_and_nature"},speak_no_evil:{keywords:["monkey","animal","nature","omg"],char:'🙊',fitzpatrick_scale:false,category:"animals_and_nature"},monkey:{keywords:["animal","nature","banana","circus"],char:'🐒',fitzpatrick_scale:false,category:"animals_and_nature"},chicken:{keywords:["animal","cluck","nature","bird"],char:'🐔',fitzpatrick_scale:false,category:"animals_and_nature"},penguin:{keywords:["animal","nature"],char:'🐧',fitzpatrick_scale:false,category:"animals_and_nature"},bird:{keywords:["animal","nature","fly","tweet","spring"],char:'🐦',fitzpatrick_scale:false,category:"animals_and_nature"},baby_chick:{keywords:["animal","chicken","bird"],char:'🐤',fitzpatrick_scale:false,category:"animals_and_nature"},hatching_chick:{keywords:["animal","chicken","egg","born","baby","bird"],char:'🐣',fitzpatrick_scale:false,category:"animals_and_nature"},hatched_chick:{keywords:["animal","chicken","baby","bird"],char:'🐥',fitzpatrick_scale:false,category:"animals_and_nature"},duck:{keywords:["animal","nature","bird","mallard"],char:'🦆',fitzpatrick_scale:false,category:"animals_and_nature"},eagle:{keywords:["animal","nature","bird"],char:'🦅',fitzpatrick_scale:false,category:"animals_and_nature"},owl:{keywords:["animal","nature","bird","hoot"],char:'🦉',fitzpatrick_scale:false,category:"animals_and_nature"},bat:{keywords:["animal","nature","blind","vampire"],char:'🦇',fitzpatrick_scale:false,category:"animals_and_nature"},wolf:{keywords:["animal","nature","wild"],char:'🐺',fitzpatrick_scale:false,category:"animals_and_nature"},boar:{keywords:["animal","nature"],char:'🐗',fitzpatrick_scale:false,category:"animals_and_nature"},horse:{keywords:["animal","brown","nature"],char:'🐴',fitzpatrick_scale:false,category:"animals_and_nature"},unicorn:{keywords:["animal","nature","mystical"],char:'🦄',fitzpatrick_scale:false,category:"animals_and_nature"},honeybee:{keywords:["animal","insect","nature","bug","spring","honey"],char:'🐝',fitzpatrick_scale:false,category:"animals_and_nature"},bug:{keywords:["animal","insect","nature","worm"],char:'🐛',fitzpatrick_scale:false,category:"animals_and_nature"},butterfly:{keywords:["animal","insect","nature","caterpillar"],char:'🦋',fitzpatrick_scale:false,category:"animals_and_nature"},snail:{keywords:["slow","animal","shell"],char:'🐌',fitzpatrick_scale:false,category:"animals_and_nature"},beetle:{keywords:["animal","insect","nature","ladybug"],char:'🐞',fitzpatrick_scale:false,category:"animals_and_nature"},ant:{keywords:["animal","insect","nature","bug"],char:'🐜',fitzpatrick_scale:false,category:"animals_and_nature"},grasshopper:{keywords:["animal","cricket","chirp"],char:'🦗',fitzpatrick_scale:false,category:"animals_and_nature"},spider:{keywords:["animal","arachnid"],char:'🕷',fitzpatrick_scale:false,category:"animals_and_nature"},scorpion:{keywords:["animal","arachnid"],char:'🦂',fitzpatrick_scale:false,category:"animals_and_nature"},crab:{keywords:["animal","crustacean"],char:'🦀',fitzpatrick_scale:false,category:"animals_and_nature"},snake:{keywords:["animal","evil","nature","hiss","python"],char:'🐍',fitzpatrick_scale:false,category:"animals_and_nature"},lizard:{keywords:["animal","nature","reptile"],char:'🦎',fitzpatrick_scale:false,category:"animals_and_nature"},"t-rex":{keywords:["animal","nature","dinosaur","tyrannosaurus","extinct"],char:'🦖',fitzpatrick_scale:false,category:"animals_and_nature"},sauropod:{keywords:["animal","nature","dinosaur","brachiosaurus","brontosaurus","diplodocus","extinct"],char:'🦕',fitzpatrick_scale:false,category:"animals_and_nature"},turtle:{keywords:["animal","slow","nature","tortoise"],char:'🐢',fitzpatrick_scale:false,category:"animals_and_nature"},tropical_fish:{keywords:["animal","swim","ocean","beach","nemo"],char:'🐠',fitzpatrick_scale:false,category:"animals_and_nature"},fish:{keywords:["animal","food","nature"],char:'🐟',fitzpatrick_scale:false,category:"animals_and_nature"},blowfish:{keywords:["animal","nature","food","sea","ocean"],char:'🐡',fitzpatrick_scale:false,category:"animals_and_nature"},dolphin:{keywords:["animal","nature","fish","sea","ocean","flipper","fins","beach"],char:'🐬',fitzpatrick_scale:false,category:"animals_and_nature"},shark:{keywords:["animal","nature","fish","sea","ocean","jaws","fins","beach"],char:'🦈',fitzpatrick_scale:false,category:"animals_and_nature"},whale:{keywords:["animal","nature","sea","ocean"],char:'🐳',fitzpatrick_scale:false,category:"animals_and_nature"},whale2:{keywords:["animal","nature","sea","ocean"],char:'🐋',fitzpatrick_scale:false,category:"animals_and_nature"},crocodile:{keywords:["animal","nature","reptile","lizard","alligator"],char:'🐊',fitzpatrick_scale:false,category:"animals_and_nature"},leopard:{keywords:["animal","nature"],char:'🐆',fitzpatrick_scale:false,category:"animals_and_nature"},zebra:{keywords:["animal","nature","stripes","safari"],char:'🦓',fitzpatrick_scale:false,category:"animals_and_nature"},tiger2:{keywords:["animal","nature","roar"],char:'🐅',fitzpatrick_scale:false,category:"animals_and_nature"},water_buffalo:{keywords:["animal","nature","ox","cow"],char:'🐃',fitzpatrick_scale:false,category:"animals_and_nature"},ox:{keywords:["animal","cow","beef"],char:'🐂',fitzpatrick_scale:false,category:"animals_and_nature"},cow2:{keywords:["beef","ox","animal","nature","moo","milk"],char:'🐄',fitzpatrick_scale:false,category:"animals_and_nature"},deer:{keywords:["animal","nature","horns","venison"],char:'🦌',fitzpatrick_scale:false,category:"animals_and_nature"},dromedary_camel:{keywords:["animal","hot","desert","hump"],char:'🐪',fitzpatrick_scale:false,category:"animals_and_nature"},camel:{keywords:["animal","nature","hot","desert","hump"],char:'🐫',fitzpatrick_scale:false,category:"animals_and_nature"},giraffe:{keywords:["animal","nature","spots","safari"],char:'🦒',fitzpatrick_scale:false,category:"animals_and_nature"},elephant:{keywords:["animal","nature","nose","th","circus"],char:'🐘',fitzpatrick_scale:false,category:"animals_and_nature"},rhinoceros:{keywords:["animal","nature","horn"],char:'🦏',fitzpatrick_scale:false,category:"animals_and_nature"},goat:{keywords:["animal","nature"],char:'🐐',fitzpatrick_scale:false,category:"animals_and_nature"},ram:{keywords:["animal","sheep","nature"],char:'🐏',fitzpatrick_scale:false,category:"animals_and_nature"},sheep:{keywords:["animal","nature","wool","shipit"],char:'🐑',fitzpatrick_scale:false,category:"animals_and_nature"},racehorse:{keywords:["animal","gamble","luck"],char:'🐎',fitzpatrick_scale:false,category:"animals_and_nature"},pig2:{keywords:["animal","nature"],char:'🐖',fitzpatrick_scale:false,category:"animals_and_nature"},rat:{keywords:["animal","mouse","rodent"],char:'🐀',fitzpatrick_scale:false,category:"animals_and_nature"},mouse2:{keywords:["animal","nature","rodent"],char:'🐁',fitzpatrick_scale:false,category:"animals_and_nature"},rooster:{keywords:["animal","nature","chicken"],char:'🐓',fitzpatrick_scale:false,category:"animals_and_nature"},turkey:{keywords:["animal","bird"],char:'🦃',fitzpatrick_scale:false,category:"animals_and_nature"},dove:{keywords:["animal","bird"],char:'🕊',fitzpatrick_scale:false,category:"animals_and_nature"},dog2:{keywords:["animal","nature","friend","doge","pet","faithful"],char:'🐕',fitzpatrick_scale:false,category:"animals_and_nature"},poodle:{keywords:["dog","animal","101","nature","pet"],char:'🐩',fitzpatrick_scale:false,category:"animals_and_nature"},cat2:{keywords:["animal","meow","pet","cats"],char:'🐈',fitzpatrick_scale:false,category:"animals_and_nature"},rabbit2:{keywords:["animal","nature","pet","magic","spring"],char:'🐇',fitzpatrick_scale:false,category:"animals_and_nature"},chipmunk:{keywords:["animal","nature","rodent","squirrel"],char:'🐿',fitzpatrick_scale:false,category:"animals_and_nature"},hedgehog:{keywords:["animal","nature","spiny"],char:'🦔',fitzpatrick_scale:false,category:"animals_and_nature"},raccoon:{keywords:["animal","nature"],char:'🦝',fitzpatrick_scale:false,category:"animals_and_nature"},llama:{keywords:["animal","nature","alpaca"],char:'🦙',fitzpatrick_scale:false,category:"animals_and_nature"},hippopotamus:{keywords:["animal","nature"],char:'🦛',fitzpatrick_scale:false,category:"animals_and_nature"},kangaroo:{keywords:["animal","nature","australia","joey","hop","marsupial"],char:'🦘',fitzpatrick_scale:false,category:"animals_and_nature"},badger:{keywords:["animal","nature","honey"],char:'🦡',fitzpatrick_scale:false,category:"animals_and_nature"},swan:{keywords:["animal","nature","bird"],char:'🦢',fitzpatrick_scale:false,category:"animals_and_nature"},peacock:{keywords:["animal","nature","peahen","bird"],char:'🦚',fitzpatrick_scale:false,category:"animals_and_nature"},parrot:{keywords:["animal","nature","bird","pirate","talk"],char:'🦜',fitzpatrick_scale:false,category:"animals_and_nature"},lobster:{keywords:["animal","nature","bisque","claws","seafood"],char:'🦞',fitzpatrick_scale:false,category:"animals_and_nature"},mosquito:{keywords:["animal","nature","insect","malaria"],char:'🦟',fitzpatrick_scale:false,category:"animals_and_nature"},paw_prints:{keywords:["animal","tracking","footprints","dog","cat","pet","feet"],char:'🐾',fitzpatrick_scale:false,category:"animals_and_nature"},dragon:{keywords:["animal","myth","nature","chinese","green"],char:'🐉',fitzpatrick_scale:false,category:"animals_and_nature"},dragon_face:{keywords:["animal","myth","nature","chinese","green"],char:'🐲',fitzpatrick_scale:false,category:"animals_and_nature"},cactus:{keywords:["vegetable","plant","nature"],char:'🌵',fitzpatrick_scale:false,category:"animals_and_nature"},christmas_tree:{keywords:["festival","vacation","december","xmas","celebration"],char:'🎄',fitzpatrick_scale:false,category:"animals_and_nature"},evergreen_tree:{keywords:["plant","nature"],char:'🌲',fitzpatrick_scale:false,category:"animals_and_nature"},deciduous_tree:{keywords:["plant","nature"],char:'🌳',fitzpatrick_scale:false,category:"animals_and_nature"},palm_tree:{keywords:["plant","vegetable","nature","summer","beach","mojito","tropical"],char:'🌴',fitzpatrick_scale:false,category:"animals_and_nature"},seedling:{keywords:["plant","nature","grass","lawn","spring"],char:'🌱',fitzpatrick_scale:false,category:"animals_and_nature"},herb:{keywords:["vegetable","plant","medicine","weed","grass","lawn"],char:'🌿',fitzpatrick_scale:false,category:"animals_and_nature"},shamrock:{keywords:["vegetable","plant","nature","irish","clover"],char:'☘',fitzpatrick_scale:false,category:"animals_and_nature"},four_leaf_clover:{keywords:["vegetable","plant","nature","lucky","irish"],char:'🍀',fitzpatrick_scale:false,category:"animals_and_nature"},bamboo:{keywords:["plant","nature","vegetable","panda","pine_decoration"],char:'🎍',fitzpatrick_scale:false,category:"animals_and_nature"},tanabata_tree:{keywords:["plant","nature","branch","summer"],char:'🎋',fitzpatrick_scale:false,category:"animals_and_nature"},leaves:{keywords:["nature","plant","tree","vegetable","grass","lawn","spring"],char:'🍃',fitzpatrick_scale:false,category:"animals_and_nature"},fallen_leaf:{keywords:["nature","plant","vegetable","leaves"],char:'🍂',fitzpatrick_scale:false,category:"animals_and_nature"},maple_leaf:{keywords:["nature","plant","vegetable","ca","fall"],char:'🍁',fitzpatrick_scale:false,category:"animals_and_nature"},ear_of_rice:{keywords:["nature","plant"],char:'🌾',fitzpatrick_scale:false,category:"animals_and_nature"},hibiscus:{keywords:["plant","vegetable","flowers","beach"],char:'🌺',fitzpatrick_scale:false,category:"animals_and_nature"},sunflower:{keywords:["nature","plant","fall"],char:'🌻',fitzpatrick_scale:false,category:"animals_and_nature"},rose:{keywords:["flowers","valentines","love","spring"],char:'🌹',fitzpatrick_scale:false,category:"animals_and_nature"},wilted_flower:{keywords:["plant","nature","flower"],char:'🥀',fitzpatrick_scale:false,category:"animals_and_nature"},tulip:{keywords:["flowers","plant","nature","summer","spring"],char:'🌷',fitzpatrick_scale:false,category:"animals_and_nature"},blossom:{keywords:["nature","flowers","yellow"],char:'🌼',fitzpatrick_scale:false,category:"animals_and_nature"},cherry_blossom:{keywords:["nature","plant","spring","flower"],char:'🌸',fitzpatrick_scale:false,category:"animals_and_nature"},bouquet:{keywords:["flowers","nature","spring"],char:'💐',fitzpatrick_scale:false,category:"animals_and_nature"},mushroom:{keywords:["plant","vegetable"],char:'🍄',fitzpatrick_scale:false,category:"animals_and_nature"},chestnut:{keywords:["food","squirrel"],char:'🌰',fitzpatrick_scale:false,category:"animals_and_nature"},jack_o_lantern:{keywords:["halloween","light","pumpkin","creepy","fall"],char:'🎃',fitzpatrick_scale:false,category:"animals_and_nature"},shell:{keywords:["nature","sea","beach"],char:'🐚',fitzpatrick_scale:false,category:"animals_and_nature"},spider_web:{keywords:["animal","insect","arachnid","silk"],char:'🕸',fitzpatrick_scale:false,category:"animals_and_nature"},earth_americas:{keywords:["globe","world","USA","international"],char:'🌎',fitzpatrick_scale:false,category:"animals_and_nature"},earth_africa:{keywords:["globe","world","international"],char:'🌍',fitzpatrick_scale:false,category:"animals_and_nature"},earth_asia:{keywords:["globe","world","east","international"],char:'🌏',fitzpatrick_scale:false,category:"animals_and_nature"},full_moon:{keywords:["nature","yellow","twilight","planet","space","night","evening","sleep"],char:'🌕',fitzpatrick_scale:false,category:"animals_and_nature"},waning_gibbous_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep","waxing_gibbous_moon"],char:'🌖',fitzpatrick_scale:false,category:"animals_and_nature"},last_quarter_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:'🌗',fitzpatrick_scale:false,category:"animals_and_nature"},waning_crescent_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:'🌘',fitzpatrick_scale:false,category:"animals_and_nature"},new_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:'🌑',fitzpatrick_scale:false,category:"animals_and_nature"},waxing_crescent_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:'🌒',fitzpatrick_scale:false,category:"animals_and_nature"},first_quarter_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:'🌓',fitzpatrick_scale:false,category:"animals_and_nature"},waxing_gibbous_moon:{keywords:["nature","night","sky","gray","twilight","planet","space","evening","sleep"],char:'🌔',fitzpatrick_scale:false,category:"animals_and_nature"},new_moon_with_face:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:'🌚',fitzpatrick_scale:false,category:"animals_and_nature"},full_moon_with_face:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:'🌝',fitzpatrick_scale:false,category:"animals_and_nature"},first_quarter_moon_with_face:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:'🌛',fitzpatrick_scale:false,category:"animals_and_nature"},last_quarter_moon_with_face:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:'🌜',fitzpatrick_scale:false,category:"animals_and_nature"},sun_with_face:{keywords:["nature","morning","sky"],char:'🌞',fitzpatrick_scale:false,category:"animals_and_nature"},crescent_moon:{keywords:["night","sleep","sky","evening","magic"],char:'🌙',fitzpatrick_scale:false,category:"animals_and_nature"},star:{keywords:["night","yellow"],char:'⭐',fitzpatrick_scale:false,category:"animals_and_nature"},star2:{keywords:["night","sparkle","awesome","good","magic"],char:'🌟',fitzpatrick_scale:false,category:"animals_and_nature"},dizzy:{keywords:["star","sparkle","shoot","magic"],char:'💫',fitzpatrick_scale:false,category:"animals_and_nature"},sparkles:{keywords:["stars","shine","shiny","cool","awesome","good","magic"],char:'✨',fitzpatrick_scale:false,category:"animals_and_nature"},comet:{keywords:["space"],char:'☄',fitzpatrick_scale:false,category:"animals_and_nature"},sunny:{keywords:["weather","nature","brightness","summer","beach","spring"],char:'☀️',fitzpatrick_scale:false,category:"animals_and_nature"},sun_behind_small_cloud:{keywords:["weather"],char:'🌤',fitzpatrick_scale:false,category:"animals_and_nature"},partly_sunny:{keywords:["weather","nature","cloudy","morning","fall","spring"],char:'⛅',fitzpatrick_scale:false,category:"animals_and_nature"},sun_behind_large_cloud:{keywords:["weather"],char:'🌥',fitzpatrick_scale:false,category:"animals_and_nature"},sun_behind_rain_cloud:{keywords:["weather"],char:'🌦',fitzpatrick_scale:false,category:"animals_and_nature"},cloud:{keywords:["weather","sky"],char:'☁️',fitzpatrick_scale:false,category:"animals_and_nature"},cloud_with_rain:{keywords:["weather"],char:'🌧',fitzpatrick_scale:false,category:"animals_and_nature"},cloud_with_lightning_and_rain:{keywords:["weather","lightning"],char:'⛈',fitzpatrick_scale:false,category:"animals_and_nature"},cloud_with_lightning:{keywords:["weather","thunder"],char:'🌩',fitzpatrick_scale:false,category:"animals_and_nature"},zap:{keywords:["thunder","weather","lightning bolt","fast"],char:'⚡',fitzpatrick_scale:false,category:"animals_and_nature"},fire:{keywords:["hot","cook","flame"],char:'🔥',fitzpatrick_scale:false,category:"animals_and_nature"},boom:{keywords:["bomb","explode","explosion","collision","blown"],char:'💥',fitzpatrick_scale:false,category:"animals_and_nature"},snowflake:{keywords:["winter","season","cold","weather","christmas","xmas"],char:'❄️',fitzpatrick_scale:false,category:"animals_and_nature"},cloud_with_snow:{keywords:["weather"],char:'🌨',fitzpatrick_scale:false,category:"animals_and_nature"},snowman:{keywords:["winter","season","cold","weather","christmas","xmas","frozen","without_snow"],char:'⛄',fitzpatrick_scale:false,category:"animals_and_nature"},snowman_with_snow:{keywords:["winter","season","cold","weather","christmas","xmas","frozen"],char:'☃',fitzpatrick_scale:false,category:"animals_and_nature"},wind_face:{keywords:["gust","air"],char:'🌬',fitzpatrick_scale:false,category:"animals_and_nature"},dash:{keywords:["wind","air","fast","shoo","fart","smoke","puff"],char:'💨',fitzpatrick_scale:false,category:"animals_and_nature"},tornado:{keywords:["weather","cyclone","twister"],char:'🌪',fitzpatrick_scale:false,category:"animals_and_nature"},fog:{keywords:["weather"],char:'🌫',fitzpatrick_scale:false,category:"animals_and_nature"},open_umbrella:{keywords:["weather","spring"],char:'☂',fitzpatrick_scale:false,category:"animals_and_nature"},umbrella:{keywords:["rainy","weather","spring"],char:'☔',fitzpatrick_scale:false,category:"animals_and_nature"},droplet:{keywords:["water","drip","faucet","spring"],char:'💧',fitzpatrick_scale:false,category:"animals_and_nature"},sweat_drops:{keywords:["water","drip","oops"],char:'💦',fitzpatrick_scale:false,category:"animals_and_nature"},ocean:{keywords:["sea","water","wave","nature","tsunami","disaster"],char:'🌊',fitzpatrick_scale:false,category:"animals_and_nature"},green_apple:{keywords:["fruit","nature"],char:'🍏',fitzpatrick_scale:false,category:"food_and_drink"},apple:{keywords:["fruit","mac","school"],char:'🍎',fitzpatrick_scale:false,category:"food_and_drink"},pear:{keywords:["fruit","nature","food"],char:'🍐',fitzpatrick_scale:false,category:"food_and_drink"},tangerine:{keywords:["food","fruit","nature","orange"],char:'🍊',fitzpatrick_scale:false,category:"food_and_drink"},lemon:{keywords:["fruit","nature"],char:'🍋',fitzpatrick_scale:false,category:"food_and_drink"},banana:{keywords:["fruit","food","monkey"],char:'🍌',fitzpatrick_scale:false,category:"food_and_drink"},watermelon:{keywords:["fruit","food","picnic","summer"],char:'🍉',fitzpatrick_scale:false,category:"food_and_drink"},grapes:{keywords:["fruit","food","wine"],char:'🍇',fitzpatrick_scale:false,category:"food_and_drink"},strawberry:{keywords:["fruit","food","nature"],char:'🍓',fitzpatrick_scale:false,category:"food_and_drink"},melon:{keywords:["fruit","nature","food"],char:'🍈',fitzpatrick_scale:false,category:"food_and_drink"},cherries:{keywords:["food","fruit"],char:'🍒',fitzpatrick_scale:false,category:"food_and_drink"},peach:{keywords:["fruit","nature","food"],char:'🍑',fitzpatrick_scale:false,category:"food_and_drink"},pineapple:{keywords:["fruit","nature","food"],char:'🍍',fitzpatrick_scale:false,category:"food_and_drink"},coconut:{keywords:["fruit","nature","food","palm"],char:'🥥',fitzpatrick_scale:false,category:"food_and_drink"},kiwi_fruit:{keywords:["fruit","food"],char:'🥝',fitzpatrick_scale:false,category:"food_and_drink"},mango:{keywords:["fruit","food","tropical"],char:'🥭',fitzpatrick_scale:false,category:"food_and_drink"},avocado:{keywords:["fruit","food"],char:'🥑',fitzpatrick_scale:false,category:"food_and_drink"},broccoli:{keywords:["fruit","food","vegetable"],char:'🥦',fitzpatrick_scale:false,category:"food_and_drink"},tomato:{keywords:["fruit","vegetable","nature","food"],char:'🍅',fitzpatrick_scale:false,category:"food_and_drink"},eggplant:{keywords:["vegetable","nature","food","aubergine"],char:'🍆',fitzpatrick_scale:false,category:"food_and_drink"},cucumber:{keywords:["fruit","food","pickle"],char:'🥒',fitzpatrick_scale:false,category:"food_and_drink"},carrot:{keywords:["vegetable","food","orange"],char:'🥕',fitzpatrick_scale:false,category:"food_and_drink"},hot_pepper:{keywords:["food","spicy","chilli","chili"],char:'🌶',fitzpatrick_scale:false,category:"food_and_drink"},potato:{keywords:["food","tuber","vegatable","starch"],char:'🥔',fitzpatrick_scale:false,category:"food_and_drink"},corn:{keywords:["food","vegetable","plant"],char:'🌽',fitzpatrick_scale:false,category:"food_and_drink"},leafy_greens:{keywords:["food","vegetable","plant","bok choy","cabbage","kale","lettuce"],char:'🥬',fitzpatrick_scale:false,category:"food_and_drink"},sweet_potato:{keywords:["food","nature"],char:'🍠',fitzpatrick_scale:false,category:"food_and_drink"},peanuts:{keywords:["food","nut"],char:'🥜',fitzpatrick_scale:false,category:"food_and_drink"},honey_pot:{keywords:["bees","sweet","kitchen"],char:'🍯',fitzpatrick_scale:false,category:"food_and_drink"},croissant:{keywords:["food","bread","french"],char:'🥐',fitzpatrick_scale:false,category:"food_and_drink"},bread:{keywords:["food","wheat","breakfast","toast"],char:'🍞',fitzpatrick_scale:false,category:"food_and_drink"},baguette_bread:{keywords:["food","bread","french"],char:'🥖',fitzpatrick_scale:false,category:"food_and_drink"},bagel:{keywords:["food","bread","bakery","schmear"],char:'🥯',fitzpatrick_scale:false,category:"food_and_drink"},pretzel:{keywords:["food","bread","twisted"],char:'🥨',fitzpatrick_scale:false,category:"food_and_drink"},cheese:{keywords:["food","chadder"],char:'🧀',fitzpatrick_scale:false,category:"food_and_drink"},egg:{keywords:["food","chicken","breakfast"],char:'🥚',fitzpatrick_scale:false,category:"food_and_drink"},bacon:{keywords:["food","breakfast","pork","pig","meat"],char:'🥓',fitzpatrick_scale:false,category:"food_and_drink"},steak:{keywords:["food","cow","meat","cut","chop","lambchop","porkchop"],char:'🥩',fitzpatrick_scale:false,category:"food_and_drink"},pancakes:{keywords:["food","breakfast","flapjacks","hotcakes"],char:'🥞',fitzpatrick_scale:false,category:"food_and_drink"},poultry_leg:{keywords:["food","meat","drumstick","bird","chicken","turkey"],char:'🍗',fitzpatrick_scale:false,category:"food_and_drink"},meat_on_bone:{keywords:["good","food","drumstick"],char:'🍖',fitzpatrick_scale:false,category:"food_and_drink"},bone:{keywords:["skeleton"],char:'🦴',fitzpatrick_scale:false,category:"food_and_drink"},fried_shrimp:{keywords:["food","animal","appetizer","summer"],char:'🍤',fitzpatrick_scale:false,category:"food_and_drink"},fried_egg:{keywords:["food","breakfast","kitchen","egg"],char:'🍳',fitzpatrick_scale:false,category:"food_and_drink"},hamburger:{keywords:["meat","fast food","beef","cheeseburger","mcdonalds","burger king"],char:'🍔',fitzpatrick_scale:false,category:"food_and_drink"},fries:{keywords:["chips","snack","fast food"],char:'🍟',fitzpatrick_scale:false,category:"food_and_drink"},stuffed_flatbread:{keywords:["food","flatbread","stuffed","gyro"],char:'🥙',fitzpatrick_scale:false,category:"food_and_drink"},hotdog:{keywords:["food","frankfurter"],char:'🌭',fitzpatrick_scale:false,category:"food_and_drink"},pizza:{keywords:["food","party"],char:'🍕',fitzpatrick_scale:false,category:"food_and_drink"},sandwich:{keywords:["food","lunch","bread"],char:'🥪',fitzpatrick_scale:false,category:"food_and_drink"},canned_food:{keywords:["food","soup"],char:'🥫',fitzpatrick_scale:false,category:"food_and_drink"},spaghetti:{keywords:["food","italian","noodle"],char:'🍝',fitzpatrick_scale:false,category:"food_and_drink"},taco:{keywords:["food","mexican"],char:'🌮',fitzpatrick_scale:false,category:"food_and_drink"},burrito:{keywords:["food","mexican"],char:'🌯',fitzpatrick_scale:false,category:"food_and_drink"},green_salad:{keywords:["food","healthy","lettuce"],char:'🥗',fitzpatrick_scale:false,category:"food_and_drink"},shallow_pan_of_food:{keywords:["food","cooking","casserole","paella"],char:'🥘',fitzpatrick_scale:false,category:"food_and_drink"},ramen:{keywords:["food","japanese","noodle","chopsticks"],char:'🍜',fitzpatrick_scale:false,category:"food_and_drink"},stew:{keywords:["food","meat","soup"],char:'🍲',fitzpatrick_scale:false,category:"food_and_drink"},fish_cake:{keywords:["food","japan","sea","beach","narutomaki","pink","swirl","kamaboko","surimi","ramen"],char:'🍥',fitzpatrick_scale:false,category:"food_and_drink"},fortune_cookie:{keywords:["food","prophecy"],char:'🥠',fitzpatrick_scale:false,category:"food_and_drink"},sushi:{keywords:["food","fish","japanese","rice"],char:'🍣',fitzpatrick_scale:false,category:"food_and_drink"},bento:{keywords:["food","japanese","box"],char:'🍱',fitzpatrick_scale:false,category:"food_and_drink"},curry:{keywords:["food","spicy","hot","indian"],char:'🍛',fitzpatrick_scale:false,category:"food_and_drink"},rice_ball:{keywords:["food","japanese"],char:'🍙',fitzpatrick_scale:false,category:"food_and_drink"},rice:{keywords:["food","china","asian"],char:'🍚',fitzpatrick_scale:false,category:"food_and_drink"},rice_cracker:{keywords:["food","japanese"],char:'🍘',fitzpatrick_scale:false,category:"food_and_drink"},oden:{keywords:["food","japanese"],char:'🍢',fitzpatrick_scale:false,category:"food_and_drink"},dango:{keywords:["food","dessert","sweet","japanese","barbecue","meat"],char:'🍡',fitzpatrick_scale:false,category:"food_and_drink"},shaved_ice:{keywords:["hot","dessert","summer"],char:'🍧',fitzpatrick_scale:false,category:"food_and_drink"},ice_cream:{keywords:["food","hot","dessert"],char:'🍨',fitzpatrick_scale:false,category:"food_and_drink"},icecream:{keywords:["food","hot","dessert","summer"],char:'🍦',fitzpatrick_scale:false,category:"food_and_drink"},pie:{keywords:["food","dessert","pastry"],char:'🥧',fitzpatrick_scale:false,category:"food_and_drink"},cake:{keywords:["food","dessert"],char:'🍰',fitzpatrick_scale:false,category:"food_and_drink"},cupcake:{keywords:["food","dessert","bakery","sweet"],char:'🧁',fitzpatrick_scale:false,category:"food_and_drink"},moon_cake:{keywords:["food","autumn"],char:'🥮',fitzpatrick_scale:false,category:"food_and_drink"},birthday:{keywords:["food","dessert","cake"],char:'🎂',fitzpatrick_scale:false,category:"food_and_drink"},custard:{keywords:["dessert","food"],char:'🍮',fitzpatrick_scale:false,category:"food_and_drink"},candy:{keywords:["snack","dessert","sweet","lolly"],char:'🍬',fitzpatrick_scale:false,category:"food_and_drink"},lollipop:{keywords:["food","snack","candy","sweet"],char:'🍭',fitzpatrick_scale:false,category:"food_and_drink"},chocolate_bar:{keywords:["food","snack","dessert","sweet"],char:'🍫',fitzpatrick_scale:false,category:"food_and_drink"},popcorn:{keywords:["food","movie theater","films","snack"],char:'🍿',fitzpatrick_scale:false,category:"food_and_drink"},dumpling:{keywords:["food","empanada","pierogi","potsticker"],char:'🥟',fitzpatrick_scale:false,category:"food_and_drink"},doughnut:{keywords:["food","dessert","snack","sweet","donut"],char:'🍩',fitzpatrick_scale:false,category:"food_and_drink"},cookie:{keywords:["food","snack","oreo","chocolate","sweet","dessert"],char:'🍪',fitzpatrick_scale:false,category:"food_and_drink"},milk_glass:{keywords:["beverage","drink","cow"],char:'🥛',fitzpatrick_scale:false,category:"food_and_drink"},beer:{keywords:["relax","beverage","drink","drunk","party","pub","summer","alcohol","booze"],char:'🍺',fitzpatrick_scale:false,category:"food_and_drink"},beers:{keywords:["relax","beverage","drink","drunk","party","pub","summer","alcohol","booze"],char:'🍻',fitzpatrick_scale:false,category:"food_and_drink"},clinking_glasses:{keywords:["beverage","drink","party","alcohol","celebrate","cheers","wine","champagne","toast"],char:'🥂',fitzpatrick_scale:false,category:"food_and_drink"},wine_glass:{keywords:["drink","beverage","drunk","alcohol","booze"],char:'🍷',fitzpatrick_scale:false,category:"food_and_drink"},tumbler_glass:{keywords:["drink","beverage","drunk","alcohol","liquor","booze","bourbon","scotch","whisky","glass","shot"],char:'🥃',fitzpatrick_scale:false,category:"food_and_drink"},cocktail:{keywords:["drink","drunk","alcohol","beverage","booze","mojito"],char:'🍸',fitzpatrick_scale:false,category:"food_and_drink"},tropical_drink:{keywords:["beverage","cocktail","summer","beach","alcohol","booze","mojito"],char:'🍹',fitzpatrick_scale:false,category:"food_and_drink"},champagne:{keywords:["drink","wine","bottle","celebration"],char:'🍾',fitzpatrick_scale:false,category:"food_and_drink"},sake:{keywords:["wine","drink","drunk","beverage","japanese","alcohol","booze"],char:'🍶',fitzpatrick_scale:false,category:"food_and_drink"},tea:{keywords:["drink","bowl","breakfast","green","british"],char:'🍵',fitzpatrick_scale:false,category:"food_and_drink"},cup_with_straw:{keywords:["drink","soda"],char:'🥤',fitzpatrick_scale:false,category:"food_and_drink"},coffee:{keywords:["beverage","caffeine","latte","espresso"],char:'☕',fitzpatrick_scale:false,category:"food_and_drink"},baby_bottle:{keywords:["food","container","milk"],char:'🍼',fitzpatrick_scale:false,category:"food_and_drink"},salt:{keywords:["condiment","shaker"],char:'🧂',fitzpatrick_scale:false,category:"food_and_drink"},spoon:{keywords:["cutlery","kitchen","tableware"],char:'🥄',fitzpatrick_scale:false,category:"food_and_drink"},fork_and_knife:{keywords:["cutlery","kitchen"],char:'🍴',fitzpatrick_scale:false,category:"food_and_drink"},plate_with_cutlery:{keywords:["food","eat","meal","lunch","dinner","restaurant"],char:'🍽',fitzpatrick_scale:false,category:"food_and_drink"},bowl_with_spoon:{keywords:["food","breakfast","cereal","oatmeal","porridge"],char:'🥣',fitzpatrick_scale:false,category:"food_and_drink"},takeout_box:{keywords:["food","leftovers"],char:'🥡',fitzpatrick_scale:false,category:"food_and_drink"},chopsticks:{keywords:["food"],char:'🥢',fitzpatrick_scale:false,category:"food_and_drink"},soccer:{keywords:["sports","football"],char:'⚽',fitzpatrick_scale:false,category:"activity"},basketball:{keywords:["sports","balls","NBA"],char:'🏀',fitzpatrick_scale:false,category:"activity"},football:{keywords:["sports","balls","NFL"],char:'🏈',fitzpatrick_scale:false,category:"activity"},baseball:{keywords:["sports","balls"],char:'⚾',fitzpatrick_scale:false,category:"activity"},softball:{keywords:["sports","balls"],char:'🥎',fitzpatrick_scale:false,category:"activity"},tennis:{keywords:["sports","balls","green"],char:'🎾',fitzpatrick_scale:false,category:"activity"},volleyball:{keywords:["sports","balls"],char:'🏐',fitzpatrick_scale:false,category:"activity"},rugby_football:{keywords:["sports","team"],char:'🏉',fitzpatrick_scale:false,category:"activity"},flying_disc:{keywords:["sports","frisbee","ultimate"],char:'🥏',fitzpatrick_scale:false,category:"activity"},"8ball":{keywords:["pool","hobby","game","luck","magic"],char:'🎱',fitzpatrick_scale:false,category:"activity"},golf:{keywords:["sports","business","flag","hole","summer"],char:'⛳',fitzpatrick_scale:false,category:"activity"},golfing_woman:{keywords:["sports","business","woman","female"],char:'🏌️‍♀️',fitzpatrick_scale:false,category:"activity"},golfing_man:{keywords:["sports","business"],char:'🏌',fitzpatrick_scale:true,category:"activity"},ping_pong:{keywords:["sports","pingpong"],char:'🏓',fitzpatrick_scale:false,category:"activity"},badminton:{keywords:["sports"],char:'🏸',fitzpatrick_scale:false,category:"activity"},goal_net:{keywords:["sports"],char:'🥅',fitzpatrick_scale:false,category:"activity"},ice_hockey:{keywords:["sports"],char:'🏒',fitzpatrick_scale:false,category:"activity"},field_hockey:{keywords:["sports"],char:'🏑',fitzpatrick_scale:false,category:"activity"},lacrosse:{keywords:["sports","ball","stick"],char:'🥍',fitzpatrick_scale:false,category:"activity"},cricket:{keywords:["sports"],char:'🏏',fitzpatrick_scale:false,category:"activity"},ski:{keywords:["sports","winter","cold","snow"],char:'🎿',fitzpatrick_scale:false,category:"activity"},skier:{keywords:["sports","winter","snow"],char:'⛷',fitzpatrick_scale:false,category:"activity"},snowboarder:{keywords:["sports","winter"],char:'🏂',fitzpatrick_scale:true,category:"activity"},person_fencing:{keywords:["sports","fencing","sword"],char:'🤺',fitzpatrick_scale:false,category:"activity"},women_wrestling:{keywords:["sports","wrestlers"],char:'🤼‍♀️',fitzpatrick_scale:false,category:"activity"},men_wrestling:{keywords:["sports","wrestlers"],char:'🤼‍♂️',fitzpatrick_scale:false,category:"activity"},woman_cartwheeling:{keywords:["gymnastics"],char:'🤸‍♀️',fitzpatrick_scale:true,category:"activity"},man_cartwheeling:{keywords:["gymnastics"],char:'🤸‍♂️',fitzpatrick_scale:true,category:"activity"},woman_playing_handball:{keywords:["sports"],char:'🤾‍♀️',fitzpatrick_scale:true,category:"activity"},man_playing_handball:{keywords:["sports"],char:'🤾‍♂️',fitzpatrick_scale:true,category:"activity"},ice_skate:{keywords:["sports"],char:'⛸',fitzpatrick_scale:false,category:"activity"},curling_stone:{keywords:["sports"],char:'🥌',fitzpatrick_scale:false,category:"activity"},skateboard:{keywords:["board"],char:'🛹',fitzpatrick_scale:false,category:"activity"},sled:{keywords:["sleigh","luge","toboggan"],char:'🛷',fitzpatrick_scale:false,category:"activity"},bow_and_arrow:{keywords:["sports"],char:'🏹',fitzpatrick_scale:false,category:"activity"},fishing_pole_and_fish:{keywords:["food","hobby","summer"],char:'🎣',fitzpatrick_scale:false,category:"activity"},boxing_glove:{keywords:["sports","fighting"],char:'🥊',fitzpatrick_scale:false,category:"activity"},martial_arts_uniform:{keywords:["judo","karate","taekwondo"],char:'🥋',fitzpatrick_scale:false,category:"activity"},rowing_woman:{keywords:["sports","hobby","water","ship","woman","female"],char:'🚣‍♀️',fitzpatrick_scale:true,category:"activity"},rowing_man:{keywords:["sports","hobby","water","ship"],char:'🚣',fitzpatrick_scale:true,category:"activity"},climbing_woman:{keywords:["sports","hobby","woman","female","rock"],char:'🧗‍♀️',fitzpatrick_scale:true,category:"activity"},climbing_man:{keywords:["sports","hobby","man","male","rock"],char:'🧗‍♂️',fitzpatrick_scale:true,category:"activity"},swimming_woman:{keywords:["sports","exercise","human","athlete","water","summer","woman","female"],char:'🏊‍♀️',fitzpatrick_scale:true,category:"activity"},swimming_man:{keywords:["sports","exercise","human","athlete","water","summer"],char:'🏊',fitzpatrick_scale:true,category:"activity"},woman_playing_water_polo:{keywords:["sports","pool"],char:'🤽‍♀️',fitzpatrick_scale:true,category:"activity"},man_playing_water_polo:{keywords:["sports","pool"],char:'🤽‍♂️',fitzpatrick_scale:true,category:"activity"},woman_in_lotus_position:{keywords:["woman","female","meditation","yoga","serenity","zen","mindfulness"],char:'🧘‍♀️',fitzpatrick_scale:true,category:"activity"},man_in_lotus_position:{keywords:["man","male","meditation","yoga","serenity","zen","mindfulness"],char:'🧘‍♂️',fitzpatrick_scale:true,category:"activity"},surfing_woman:{keywords:["sports","ocean","sea","summer","beach","woman","female"],char:'🏄‍♀️',fitzpatrick_scale:true,category:"activity"},surfing_man:{keywords:["sports","ocean","sea","summer","beach"],char:'🏄',fitzpatrick_scale:true,category:"activity"},bath:{keywords:["clean","shower","bathroom"],char:'🛀',fitzpatrick_scale:true,category:"activity"},basketball_woman:{keywords:["sports","human","woman","female"],char:'⛹️‍♀️',fitzpatrick_scale:true,category:"activity"},basketball_man:{keywords:["sports","human"],char:'⛹',fitzpatrick_scale:true,category:"activity"},weight_lifting_woman:{keywords:["sports","training","exercise","woman","female"],char:'🏋️‍♀️',fitzpatrick_scale:true,category:"activity"},weight_lifting_man:{keywords:["sports","training","exercise"],char:'🏋',fitzpatrick_scale:true,category:"activity"},biking_woman:{keywords:["sports","bike","exercise","hipster","woman","female"],char:'🚴‍♀️',fitzpatrick_scale:true,category:"activity"},biking_man:{keywords:["sports","bike","exercise","hipster"],char:'🚴',fitzpatrick_scale:true,category:"activity"},mountain_biking_woman:{keywords:["transportation","sports","human","race","bike","woman","female"],char:'🚵‍♀️',fitzpatrick_scale:true,category:"activity"},mountain_biking_man:{keywords:["transportation","sports","human","race","bike"],char:'🚵',fitzpatrick_scale:true,category:"activity"},horse_racing:{keywords:["animal","betting","competition","gambling","luck"],char:'🏇',fitzpatrick_scale:true,category:"activity"},business_suit_levitating:{keywords:["suit","business","levitate","hover","jump"],char:'🕴',fitzpatrick_scale:true,category:"activity"},trophy:{keywords:["win","award","contest","place","ftw","ceremony"],char:'🏆',fitzpatrick_scale:false,category:"activity"},running_shirt_with_sash:{keywords:["play","pageant"],char:'🎽',fitzpatrick_scale:false,category:"activity"},medal_sports:{keywords:["award","winning"],char:'🏅',fitzpatrick_scale:false,category:"activity"},medal_military:{keywords:["award","winning","army"],char:'🎖',fitzpatrick_scale:false,category:"activity"},"1st_place_medal":{keywords:["award","winning","first"],char:'🥇',fitzpatrick_scale:false,category:"activity"},"2nd_place_medal":{keywords:["award","second"],char:'🥈',fitzpatrick_scale:false,category:"activity"},"3rd_place_medal":{keywords:["award","third"],char:'🥉',fitzpatrick_scale:false,category:"activity"},reminder_ribbon:{keywords:["sports","cause","support","awareness"],char:'🎗',fitzpatrick_scale:false,category:"activity"},rosette:{keywords:["flower","decoration","military"],char:'🏵',fitzpatrick_scale:false,category:"activity"},ticket:{keywords:["event","concert","pass"],char:'🎫',fitzpatrick_scale:false,category:"activity"},tickets:{keywords:["sports","concert","entrance"],char:'🎟',fitzpatrick_scale:false,category:"activity"},performing_arts:{keywords:["acting","theater","drama"],char:'🎭',fitzpatrick_scale:false,category:"activity"},art:{keywords:["design","paint","draw","colors"],char:'🎨',fitzpatrick_scale:false,category:"activity"},circus_tent:{keywords:["festival","carnival","party"],char:'🎪',fitzpatrick_scale:false,category:"activity"},woman_juggling:{keywords:["juggle","balance","skill","multitask"],char:'🤹‍♀️',fitzpatrick_scale:true,category:"activity"},man_juggling:{keywords:["juggle","balance","skill","multitask"],char:'🤹‍♂️',fitzpatrick_scale:true,category:"activity"},microphone:{keywords:["sound","music","PA","sing","talkshow"],char:'🎤',fitzpatrick_scale:false,category:"activity"},headphones:{keywords:["music","score","gadgets"],char:'🎧',fitzpatrick_scale:false,category:"activity"},musical_score:{keywords:["treble","clef","compose"],char:'🎼',fitzpatrick_scale:false,category:"activity"},musical_keyboard:{keywords:["piano","instrument","compose"],char:'🎹',fitzpatrick_scale:false,category:"activity"},drum:{keywords:["music","instrument","drumsticks","snare"],char:'🥁',fitzpatrick_scale:false,category:"activity"},saxophone:{keywords:["music","instrument","jazz","blues"],char:'🎷',fitzpatrick_scale:false,category:"activity"},trumpet:{keywords:["music","brass"],char:'🎺',fitzpatrick_scale:false,category:"activity"},guitar:{keywords:["music","instrument"],char:'🎸',fitzpatrick_scale:false,category:"activity"},violin:{keywords:["music","instrument","orchestra","symphony"],char:'🎻',fitzpatrick_scale:false,category:"activity"},clapper:{keywords:["movie","film","record"],char:'🎬',fitzpatrick_scale:false,category:"activity"},video_game:{keywords:["play","console","PS4","controller"],char:'🎮',fitzpatrick_scale:false,category:"activity"},space_invader:{keywords:["game","arcade","play"],char:'👾',fitzpatrick_scale:false,category:"activity"},dart:{keywords:["game","play","bar","target","bullseye"],char:'🎯',fitzpatrick_scale:false,category:"activity"},game_die:{keywords:["dice","random","tabletop","play","luck"],char:'🎲',fitzpatrick_scale:false,category:"activity"},chess_pawn:{keywords:["expendable"],char:"♟",fitzpatrick_scale:false,category:"activity"},slot_machine:{keywords:["bet","gamble","vegas","fruit machine","luck","casino"],char:'🎰',fitzpatrick_scale:false,category:"activity"},jigsaw:{keywords:["interlocking","puzzle","piece"],char:'🧩',fitzpatrick_scale:false,category:"activity"},bowling:{keywords:["sports","fun","play"],char:'🎳',fitzpatrick_scale:false,category:"activity"},red_car:{keywords:["red","transportation","vehicle"],char:'🚗',fitzpatrick_scale:false,category:"travel_and_places"},taxi:{keywords:["uber","vehicle","cars","transportation"],char:'🚕',fitzpatrick_scale:false,category:"travel_and_places"},blue_car:{keywords:["transportation","vehicle"],char:'🚙',fitzpatrick_scale:false,category:"travel_and_places"},bus:{keywords:["car","vehicle","transportation"],char:'🚌',fitzpatrick_scale:false,category:"travel_and_places"},trolleybus:{keywords:["bart","transportation","vehicle"],char:'🚎',fitzpatrick_scale:false,category:"travel_and_places"},racing_car:{keywords:["sports","race","fast","formula","f1"],char:'🏎',fitzpatrick_scale:false,category:"travel_and_places"},police_car:{keywords:["vehicle","cars","transportation","law","legal","enforcement"],char:'🚓',fitzpatrick_scale:false,category:"travel_and_places"},ambulance:{keywords:["health","911","hospital"],char:'🚑',fitzpatrick_scale:false,category:"travel_and_places"},fire_engine:{keywords:["transportation","cars","vehicle"],char:'🚒',fitzpatrick_scale:false,category:"travel_and_places"},minibus:{keywords:["vehicle","car","transportation"],char:'🚐',fitzpatrick_scale:false,category:"travel_and_places"},truck:{keywords:["cars","transportation"],char:'🚚',fitzpatrick_scale:false,category:"travel_and_places"},articulated_lorry:{keywords:["vehicle","cars","transportation","express"],char:'🚛',fitzpatrick_scale:false,category:"travel_and_places"},tractor:{keywords:["vehicle","car","farming","agriculture"],char:'🚜',fitzpatrick_scale:false,category:"travel_and_places"},kick_scooter:{keywords:["vehicle","kick","razor"],char:'🛴',fitzpatrick_scale:false,category:"travel_and_places"},motorcycle:{keywords:["race","sports","fast"],char:'🏍',fitzpatrick_scale:false,category:"travel_and_places"},bike:{keywords:["sports","bicycle","exercise","hipster"],char:'🚲',fitzpatrick_scale:false,category:"travel_and_places"},motor_scooter:{keywords:["vehicle","vespa","sasha"],char:'🛵',fitzpatrick_scale:false,category:"travel_and_places"},rotating_light:{keywords:["police","ambulance","911","emergency","alert","error","pinged","law","legal"],char:'🚨',fitzpatrick_scale:false,category:"travel_and_places"},oncoming_police_car:{keywords:["vehicle","law","legal","enforcement","911"],char:'🚔',fitzpatrick_scale:false,category:"travel_and_places"},oncoming_bus:{keywords:["vehicle","transportation"],char:'🚍',fitzpatrick_scale:false,category:"travel_and_places"},oncoming_automobile:{keywords:["car","vehicle","transportation"],char:'🚘',fitzpatrick_scale:false,category:"travel_and_places"},oncoming_taxi:{keywords:["vehicle","cars","uber"],char:'🚖',fitzpatrick_scale:false,category:"travel_and_places"},aerial_tramway:{keywords:["transportation","vehicle","ski"],char:'🚡',fitzpatrick_scale:false,category:"travel_and_places"},mountain_cableway:{keywords:["transportation","vehicle","ski"],char:'🚠',fitzpatrick_scale:false,category:"travel_and_places"},suspension_railway:{keywords:["vehicle","transportation"],char:'🚟',fitzpatrick_scale:false,category:"travel_and_places"},railway_car:{keywords:["transportation","vehicle"],char:'🚃',fitzpatrick_scale:false,category:"travel_and_places"},train:{keywords:["transportation","vehicle","carriage","public","travel"],char:'🚋',fitzpatrick_scale:false,category:"travel_and_places"},monorail:{keywords:["transportation","vehicle"],char:'🚝',fitzpatrick_scale:false,category:"travel_and_places"},bullettrain_side:{keywords:["transportation","vehicle"],char:'🚄',fitzpatrick_scale:false,category:"travel_and_places"},bullettrain_front:{keywords:["transportation","vehicle","speed","fast","public","travel"],char:'🚅',fitzpatrick_scale:false,category:"travel_and_places"},light_rail:{keywords:["transportation","vehicle"],char:'🚈',fitzpatrick_scale:false,category:"travel_and_places"},mountain_railway:{keywords:["transportation","vehicle"],char:'🚞',fitzpatrick_scale:false,category:"travel_and_places"},steam_locomotive:{keywords:["transportation","vehicle","train"],char:'🚂',fitzpatrick_scale:false,category:"travel_and_places"},train2:{keywords:["transportation","vehicle"],char:'🚆',fitzpatrick_scale:false,category:"travel_and_places"},metro:{keywords:["transportation","blue-square","mrt","underground","tube"],char:'🚇',fitzpatrick_scale:false,category:"travel_and_places"},tram:{keywords:["transportation","vehicle"],char:'🚊',fitzpatrick_scale:false,category:"travel_and_places"},station:{keywords:["transportation","vehicle","public"],char:'🚉',fitzpatrick_scale:false,category:"travel_and_places"},flying_saucer:{keywords:["transportation","vehicle","ufo"],char:'🛸',fitzpatrick_scale:false,category:"travel_and_places"},helicopter:{keywords:["transportation","vehicle","fly"],char:'🚁',fitzpatrick_scale:false,category:"travel_and_places"},small_airplane:{keywords:["flight","transportation","fly","vehicle"],char:'🛩',fitzpatrick_scale:false,category:"travel_and_places"},airplane:{keywords:["vehicle","transportation","flight","fly"],char:'✈️',fitzpatrick_scale:false,category:"travel_and_places"},flight_departure:{keywords:["airport","flight","landing"],char:'🛫',fitzpatrick_scale:false,category:"travel_and_places"},flight_arrival:{keywords:["airport","flight","boarding"],char:'🛬',fitzpatrick_scale:false,category:"travel_and_places"},sailboat:{keywords:["ship","summer","transportation","water","sailing"],char:'⛵',fitzpatrick_scale:false,category:"travel_and_places"},motor_boat:{keywords:["ship"],char:'🛥',fitzpatrick_scale:false,category:"travel_and_places"},speedboat:{keywords:["ship","transportation","vehicle","summer"],char:'🚤',fitzpatrick_scale:false,category:"travel_and_places"},ferry:{keywords:["boat","ship","yacht"],char:'⛴',fitzpatrick_scale:false,category:"travel_and_places"},passenger_ship:{keywords:["yacht","cruise","ferry"],char:'🛳',fitzpatrick_scale:false,category:"travel_and_places"},rocket:{keywords:["launch","ship","staffmode","NASA","outer space","outer_space","fly"],char:'🚀',fitzpatrick_scale:false,category:"travel_and_places"},artificial_satellite:{keywords:["communication","gps","orbit","spaceflight","NASA","ISS"],char:'🛰',fitzpatrick_scale:false,category:"travel_and_places"},seat:{keywords:["sit","airplane","transport","bus","flight","fly"],char:'💺',fitzpatrick_scale:false,category:"travel_and_places"},canoe:{keywords:["boat","paddle","water","ship"],char:'🛶',fitzpatrick_scale:false,category:"travel_and_places"},anchor:{keywords:["ship","ferry","sea","boat"],char:'⚓',fitzpatrick_scale:false,category:"travel_and_places"},construction:{keywords:["wip","progress","caution","warning"],char:'🚧',fitzpatrick_scale:false,category:"travel_and_places"},fuelpump:{keywords:["gas station","petroleum"],char:'⛽',fitzpatrick_scale:false,category:"travel_and_places"},busstop:{keywords:["transportation","wait"],char:'🚏',fitzpatrick_scale:false,category:"travel_and_places"},vertical_traffic_light:{keywords:["transportation","driving"],char:'🚦',fitzpatrick_scale:false,category:"travel_and_places"},traffic_light:{keywords:["transportation","signal"],char:'🚥',fitzpatrick_scale:false,category:"travel_and_places"},checkered_flag:{keywords:["contest","finishline","race","gokart"],char:'🏁',fitzpatrick_scale:false,category:"travel_and_places"},ship:{keywords:["transportation","titanic","deploy"],char:'🚢',fitzpatrick_scale:false,category:"travel_and_places"},ferris_wheel:{keywords:["photo","carnival","londoneye"],char:'🎡',fitzpatrick_scale:false,category:"travel_and_places"},roller_coaster:{keywords:["carnival","playground","photo","fun"],char:'🎢',fitzpatrick_scale:false,category:"travel_and_places"},carousel_horse:{keywords:["photo","carnival"],char:'🎠',fitzpatrick_scale:false,category:"travel_and_places"},building_construction:{keywords:["wip","working","progress"],char:'🏗',fitzpatrick_scale:false,category:"travel_and_places"},foggy:{keywords:["photo","mountain"],char:'🌁',fitzpatrick_scale:false,category:"travel_and_places"},tokyo_tower:{keywords:["photo","japanese"],char:'🗼',fitzpatrick_scale:false,category:"travel_and_places"},factory:{keywords:["building","industry","pollution","smoke"],char:'🏭',fitzpatrick_scale:false,category:"travel_and_places"},fountain:{keywords:["photo","summer","water","fresh"],char:'⛲',fitzpatrick_scale:false,category:"travel_and_places"},rice_scene:{keywords:["photo","japan","asia","tsukimi"],char:'🎑',fitzpatrick_scale:false,category:"travel_and_places"},mountain:{keywords:["photo","nature","environment"],char:'⛰',fitzpatrick_scale:false,category:"travel_and_places"},mountain_snow:{keywords:["photo","nature","environment","winter","cold"],char:'🏔',fitzpatrick_scale:false,category:"travel_and_places"},mount_fuji:{keywords:["photo","mountain","nature","japanese"],char:'🗻',fitzpatrick_scale:false,category:"travel_and_places"},volcano:{keywords:["photo","nature","disaster"],char:'🌋',fitzpatrick_scale:false,category:"travel_and_places"},japan:{keywords:["nation","country","japanese","asia"],char:'🗾',fitzpatrick_scale:false,category:"travel_and_places"},camping:{keywords:["photo","outdoors","tent"],char:'🏕',fitzpatrick_scale:false,category:"travel_and_places"},tent:{keywords:["photo","camping","outdoors"],char:'⛺',fitzpatrick_scale:false,category:"travel_and_places"},national_park:{keywords:["photo","environment","nature"],char:'🏞',fitzpatrick_scale:false,category:"travel_and_places"},motorway:{keywords:["road","cupertino","interstate","highway"],char:'🛣',fitzpatrick_scale:false,category:"travel_and_places"},railway_track:{keywords:["train","transportation"],char:'🛤',fitzpatrick_scale:false,category:"travel_and_places"},sunrise:{keywords:["morning","view","vacation","photo"],char:'🌅',fitzpatrick_scale:false,category:"travel_and_places"},sunrise_over_mountains:{keywords:["view","vacation","photo"],char:'🌄',fitzpatrick_scale:false,category:"travel_and_places"},desert:{keywords:["photo","warm","saharah"],char:'🏜',fitzpatrick_scale:false,category:"travel_and_places"},beach_umbrella:{keywords:["weather","summer","sunny","sand","mojito"],char:'🏖',fitzpatrick_scale:false,category:"travel_and_places"},desert_island:{keywords:["photo","tropical","mojito"],char:'🏝',fitzpatrick_scale:false,category:"travel_and_places"},city_sunrise:{keywords:["photo","good morning","dawn"],char:'🌇',fitzpatrick_scale:false,category:"travel_and_places"},city_sunset:{keywords:["photo","evening","sky","buildings"],char:'🌆',fitzpatrick_scale:false,category:"travel_and_places"},cityscape:{keywords:["photo","night life","urban"],char:'🏙',fitzpatrick_scale:false,category:"travel_and_places"},night_with_stars:{keywords:["evening","city","downtown"],char:'🌃',fitzpatrick_scale:false,category:"travel_and_places"},bridge_at_night:{keywords:["photo","sanfrancisco"],char:'🌉',fitzpatrick_scale:false,category:"travel_and_places"},milky_way:{keywords:["photo","space","stars"],char:'🌌',fitzpatrick_scale:false,category:"travel_and_places"},stars:{keywords:["night","photo"],char:'🌠',fitzpatrick_scale:false,category:"travel_and_places"},sparkler:{keywords:["stars","night","shine"],char:'🎇',fitzpatrick_scale:false,category:"travel_and_places"},fireworks:{keywords:["photo","festival","carnival","congratulations"],char:'🎆',fitzpatrick_scale:false,category:"travel_and_places"},rainbow:{keywords:["nature","happy","unicorn_face","photo","sky","spring"],char:'🌈',fitzpatrick_scale:false,category:"travel_and_places"},houses:{keywords:["buildings","photo"],char:'🏘',fitzpatrick_scale:false,category:"travel_and_places"},european_castle:{keywords:["building","royalty","history"],char:'🏰',fitzpatrick_scale:false,category:"travel_and_places"},japanese_castle:{keywords:["photo","building"],char:'🏯',fitzpatrick_scale:false,category:"travel_and_places"},stadium:{keywords:["photo","place","sports","concert","venue"],char:'🏟',fitzpatrick_scale:false,category:"travel_and_places"},statue_of_liberty:{keywords:["american","newyork"],char:'🗽',fitzpatrick_scale:false,category:"travel_and_places"},house:{keywords:["building","home"],char:'🏠',fitzpatrick_scale:false,category:"travel_and_places"},house_with_garden:{keywords:["home","plant","nature"],char:'🏡',fitzpatrick_scale:false,category:"travel_and_places"},derelict_house:{keywords:["abandon","evict","broken","building"],char:'🏚',fitzpatrick_scale:false,category:"travel_and_places"},office:{keywords:["building","bureau","work"],char:'🏢',fitzpatrick_scale:false,category:"travel_and_places"},department_store:{keywords:["building","shopping","mall"],char:'🏬',fitzpatrick_scale:false,category:"travel_and_places"},post_office:{keywords:["building","envelope","communication"],char:'🏣',fitzpatrick_scale:false,category:"travel_and_places"},european_post_office:{keywords:["building","email"],char:'🏤',fitzpatrick_scale:false,category:"travel_and_places"},hospital:{keywords:["building","health","surgery","doctor"],char:'🏥',fitzpatrick_scale:false,category:"travel_and_places"},bank:{keywords:["building","money","sales","cash","business","enterprise"],char:'🏦',fitzpatrick_scale:false,category:"travel_and_places"},hotel:{keywords:["building","accomodation","checkin"],char:'🏨',fitzpatrick_scale:false,category:"travel_and_places"},convenience_store:{keywords:["building","shopping","groceries"],char:'🏪',fitzpatrick_scale:false,category:"travel_and_places"},school:{keywords:["building","student","education","learn","teach"],char:'🏫',fitzpatrick_scale:false,category:"travel_and_places"},love_hotel:{keywords:["like","affection","dating"],char:'🏩',fitzpatrick_scale:false,category:"travel_and_places"},wedding:{keywords:["love","like","affection","couple","marriage","bride","groom"],char:'💒',fitzpatrick_scale:false,category:"travel_and_places"},classical_building:{keywords:["art","culture","history"],char:'🏛',fitzpatrick_scale:false,category:"travel_and_places"},church:{keywords:["building","religion","christ"],char:'⛪',fitzpatrick_scale:false,category:"travel_and_places"},mosque:{keywords:["islam","worship","minaret"],char:'🕌',fitzpatrick_scale:false,category:"travel_and_places"},synagogue:{keywords:["judaism","worship","temple","jewish"],char:'🕍',fitzpatrick_scale:false,category:"travel_and_places"},kaaba:{keywords:["mecca","mosque","islam"],char:'🕋',fitzpatrick_scale:false,category:"travel_and_places"},shinto_shrine:{keywords:["temple","japan","kyoto"],char:'⛩',fitzpatrick_scale:false,category:"travel_and_places"},watch:{keywords:["time","accessories"],char:'⌚',fitzpatrick_scale:false,category:"objects"},iphone:{keywords:["technology","apple","gadgets","dial"],char:'📱',fitzpatrick_scale:false,category:"objects"},calling:{keywords:["iphone","incoming"],char:'📲',fitzpatrick_scale:false,category:"objects"},computer:{keywords:["technology","laptop","screen","display","monitor"],char:'💻',fitzpatrick_scale:false,category:"objects"},keyboard:{keywords:["technology","computer","type","input","text"],char:'⌨',fitzpatrick_scale:false,category:"objects"},desktop_computer:{keywords:["technology","computing","screen"],char:'🖥',fitzpatrick_scale:false,category:"objects"},printer:{keywords:["paper","ink"],char:'🖨',fitzpatrick_scale:false,category:"objects"},computer_mouse:{keywords:["click"],char:'🖱',fitzpatrick_scale:false,category:"objects"},trackball:{keywords:["technology","trackpad"],char:'🖲',fitzpatrick_scale:false,category:"objects"},joystick:{keywords:["game","play"],char:'🕹',fitzpatrick_scale:false,category:"objects"},clamp:{keywords:["tool"],char:'🗜',fitzpatrick_scale:false,category:"objects"},minidisc:{keywords:["technology","record","data","disk","90s"],char:'💽',fitzpatrick_scale:false,category:"objects"},floppy_disk:{keywords:["oldschool","technology","save","90s","80s"],char:'💾',fitzpatrick_scale:false,category:"objects"},cd:{keywords:["technology","dvd","disk","disc","90s"],char:'💿',fitzpatrick_scale:false,category:"objects"},dvd:{keywords:["cd","disk","disc"],char:'📀',fitzpatrick_scale:false,category:"objects"},vhs:{keywords:["record","video","oldschool","90s","80s"],char:'📼',fitzpatrick_scale:false,category:"objects"},camera:{keywords:["gadgets","photography"],char:'📷',fitzpatrick_scale:false,category:"objects"},camera_flash:{keywords:["photography","gadgets"],char:'📸',fitzpatrick_scale:false,category:"objects"},video_camera:{keywords:["film","record"],char:'📹',fitzpatrick_scale:false,category:"objects"},movie_camera:{keywords:["film","record"],char:'🎥',fitzpatrick_scale:false,category:"objects"},film_projector:{keywords:["video","tape","record","movie"],char:'📽',fitzpatrick_scale:false,category:"objects"},film_strip:{keywords:["movie"],char:'🎞',fitzpatrick_scale:false,category:"objects"},telephone_receiver:{keywords:["technology","communication","dial"],char:'📞',fitzpatrick_scale:false,category:"objects"},phone:{keywords:["technology","communication","dial","telephone"],char:'☎️',fitzpatrick_scale:false,category:"objects"},pager:{keywords:["bbcall","oldschool","90s"],char:'📟',fitzpatrick_scale:false,category:"objects"},fax:{keywords:["communication","technology"],char:'📠',fitzpatrick_scale:false,category:"objects"},tv:{keywords:["technology","program","oldschool","show","television"],char:'📺',fitzpatrick_scale:false,category:"objects"},radio:{keywords:["communication","music","podcast","program"],char:'📻',fitzpatrick_scale:false,category:"objects"},studio_microphone:{keywords:["sing","recording","artist","talkshow"],char:'🎙',fitzpatrick_scale:false,category:"objects"},level_slider:{keywords:["scale"],char:'🎚',fitzpatrick_scale:false,category:"objects"},control_knobs:{keywords:["dial"],char:'🎛',fitzpatrick_scale:false,category:"objects"},compass:{keywords:["magnetic","navigation","orienteering"],char:'🧭',fitzpatrick_scale:false,category:"objects"},stopwatch:{keywords:["time","deadline"],char:'⏱',fitzpatrick_scale:false,category:"objects"},timer_clock:{keywords:["alarm"],char:'⏲',fitzpatrick_scale:false,category:"objects"},alarm_clock:{keywords:["time","wake"],char:'⏰',fitzpatrick_scale:false,category:"objects"},mantelpiece_clock:{keywords:["time"],char:'🕰',fitzpatrick_scale:false,category:"objects"},hourglass_flowing_sand:{keywords:["oldschool","time","countdown"],char:'⏳',fitzpatrick_scale:false,category:"objects"},hourglass:{keywords:["time","clock","oldschool","limit","exam","quiz","test"],char:'⌛',fitzpatrick_scale:false,category:"objects"},satellite:{keywords:["communication","future","radio","space"],char:'📡',fitzpatrick_scale:false,category:"objects"},battery:{keywords:["power","energy","sustain"],char:'🔋',fitzpatrick_scale:false,category:"objects"},electric_plug:{keywords:["charger","power"],char:'🔌',fitzpatrick_scale:false,category:"objects"},bulb:{keywords:["light","electricity","idea"],char:'💡',fitzpatrick_scale:false,category:"objects"},flashlight:{keywords:["dark","camping","sight","night"],char:'🔦',fitzpatrick_scale:false,category:"objects"},candle:{keywords:["fire","wax"],char:'🕯',fitzpatrick_scale:false,category:"objects"},fire_extinguisher:{keywords:["quench"],char:'🧯',fitzpatrick_scale:false,category:"objects"},wastebasket:{keywords:["bin","trash","rubbish","garbage","toss"],char:'🗑',fitzpatrick_scale:false,category:"objects"},oil_drum:{keywords:["barrell"],char:'🛢',fitzpatrick_scale:false,category:"objects"},money_with_wings:{keywords:["dollar","bills","payment","sale"],char:'💸',fitzpatrick_scale:false,category:"objects"},dollar:{keywords:["money","sales","bill","currency"],char:'💵',fitzpatrick_scale:false,category:"objects"},yen:{keywords:["money","sales","japanese","dollar","currency"],char:'💴',fitzpatrick_scale:false,category:"objects"},euro:{keywords:["money","sales","dollar","currency"],char:'💶',fitzpatrick_scale:false,category:"objects"},pound:{keywords:["british","sterling","money","sales","bills","uk","england","currency"],char:'💷',fitzpatrick_scale:false,category:"objects"},moneybag:{keywords:["dollar","payment","coins","sale"],char:'💰',fitzpatrick_scale:false,category:"objects"},credit_card:{keywords:["money","sales","dollar","bill","payment","shopping"],char:'💳',fitzpatrick_scale:false,category:"objects"},gem:{keywords:["blue","ruby","diamond","jewelry"],char:'💎',fitzpatrick_scale:false,category:"objects"},balance_scale:{keywords:["law","fairness","weight"],char:'⚖',fitzpatrick_scale:false,category:"objects"},toolbox:{keywords:["tools","diy","fix","maintainer","mechanic"],char:'🧰',fitzpatrick_scale:false,category:"objects"},wrench:{keywords:["tools","diy","ikea","fix","maintainer"],char:'🔧',fitzpatrick_scale:false,category:"objects"},hammer:{keywords:["tools","build","create"],char:'🔨',fitzpatrick_scale:false,category:"objects"},hammer_and_pick:{keywords:["tools","build","create"],char:'⚒',fitzpatrick_scale:false,category:"objects"},hammer_and_wrench:{keywords:["tools","build","create"],char:'🛠',fitzpatrick_scale:false,category:"objects"},pick:{keywords:["tools","dig"],char:'⛏',fitzpatrick_scale:false,category:"objects"},nut_and_bolt:{keywords:["handy","tools","fix"],char:'🔩',fitzpatrick_scale:false,category:"objects"},gear:{keywords:["cog"],char:'⚙',fitzpatrick_scale:false,category:"objects"},brick:{keywords:["bricks"],char:'🧱',fitzpatrick_scale:false,category:"objects"},chains:{keywords:["lock","arrest"],char:'⛓',fitzpatrick_scale:false,category:"objects"},magnet:{keywords:["attraction","magnetic"],char:'🧲',fitzpatrick_scale:false,category:"objects"},gun:{keywords:["violence","weapon","pistol","revolver"],char:'🔫',fitzpatrick_scale:false,category:"objects"},bomb:{keywords:["boom","explode","explosion","terrorism"],char:'💣',fitzpatrick_scale:false,category:"objects"},firecracker:{keywords:["dynamite","boom","explode","explosion","explosive"],char:'🧨',fitzpatrick_scale:false,category:"objects"},hocho:{keywords:["knife","blade","cutlery","kitchen","weapon"],char:'🔪',fitzpatrick_scale:false,category:"objects"},dagger:{keywords:["weapon"],char:'🗡',fitzpatrick_scale:false,category:"objects"},crossed_swords:{keywords:["weapon"],char:'⚔',fitzpatrick_scale:false,category:"objects"},shield:{keywords:["protection","security"],char:'🛡',fitzpatrick_scale:false,category:"objects"},smoking:{keywords:["kills","tobacco","cigarette","joint","smoke"],char:'🚬',fitzpatrick_scale:false,category:"objects"},skull_and_crossbones:{keywords:["poison","danger","deadly","scary","death","pirate","evil"],char:'☠',fitzpatrick_scale:false,category:"objects"},coffin:{keywords:["vampire","dead","die","death","rip","graveyard","cemetery","casket","funeral","box"],char:'⚰',fitzpatrick_scale:false,category:"objects"},funeral_urn:{keywords:["dead","die","death","rip","ashes"],char:'⚱',fitzpatrick_scale:false,category:"objects"},amphora:{keywords:["vase","jar"],char:'🏺',fitzpatrick_scale:false,category:"objects"},crystal_ball:{keywords:["disco","party","magic","circus","fortune_teller"],char:'🔮',fitzpatrick_scale:false,category:"objects"},prayer_beads:{keywords:["dhikr","religious"],char:'📿',fitzpatrick_scale:false,category:"objects"},nazar_amulet:{keywords:["bead","charm"],char:'🧿',fitzpatrick_scale:false,category:"objects"},barber:{keywords:["hair","salon","style"],char:'💈',fitzpatrick_scale:false,category:"objects"},alembic:{keywords:["distilling","science","experiment","chemistry"],char:'⚗',fitzpatrick_scale:false,category:"objects"},telescope:{keywords:["stars","space","zoom","science","astronomy"],char:'🔭',fitzpatrick_scale:false,category:"objects"},microscope:{keywords:["laboratory","experiment","zoomin","science","study"],char:'🔬',fitzpatrick_scale:false,category:"objects"},hole:{keywords:["embarrassing"],char:'🕳',fitzpatrick_scale:false,category:"objects"},pill:{keywords:["health","medicine","doctor","pharmacy","drug"],char:'💊',fitzpatrick_scale:false,category:"objects"},syringe:{keywords:["health","hospital","drugs","blood","medicine","needle","doctor","nurse"],char:'💉',fitzpatrick_scale:false,category:"objects"},dna:{keywords:["biologist","genetics","life"],char:'🧬',fitzpatrick_scale:false,category:"objects"},microbe:{keywords:["amoeba","bacteria","germs"],char:'🦠',fitzpatrick_scale:false,category:"objects"},petri_dish:{keywords:["bacteria","biology","culture","lab"],char:'🧫',fitzpatrick_scale:false,category:"objects"},test_tube:{keywords:["chemistry","experiment","lab","science"],char:'🧪',fitzpatrick_scale:false,category:"objects"},thermometer:{keywords:["weather","temperature","hot","cold"],char:'🌡',fitzpatrick_scale:false,category:"objects"},broom:{keywords:["cleaning","sweeping","witch"],char:'🧹',fitzpatrick_scale:false,category:"objects"},basket:{keywords:["laundry"],char:'🧺',fitzpatrick_scale:false,category:"objects"},toilet_paper:{keywords:["roll"],char:'🧻',fitzpatrick_scale:false,category:"objects"},label:{keywords:["sale","tag"],char:'🏷',fitzpatrick_scale:false,category:"objects"},bookmark:{keywords:["favorite","label","save"],char:'🔖',fitzpatrick_scale:false,category:"objects"},toilet:{keywords:["restroom","wc","washroom","bathroom","potty"],char:'🚽',fitzpatrick_scale:false,category:"objects"},shower:{keywords:["clean","water","bathroom"],char:'🚿',fitzpatrick_scale:false,category:"objects"},bathtub:{keywords:["clean","shower","bathroom"],char:'🛁',fitzpatrick_scale:false,category:"objects"},soap:{keywords:["bar","bathing","cleaning","lather"],char:'🧼',fitzpatrick_scale:false,category:"objects"},sponge:{keywords:["absorbing","cleaning","porous"],char:'🧽',fitzpatrick_scale:false,category:"objects"},lotion_bottle:{keywords:["moisturizer","sunscreen"],char:'🧴',fitzpatrick_scale:false,category:"objects"},key:{keywords:["lock","door","password"],char:'🔑',fitzpatrick_scale:false,category:"objects"},old_key:{keywords:["lock","door","password"],char:'🗝',fitzpatrick_scale:false,category:"objects"},couch_and_lamp:{keywords:["read","chill"],char:'🛋',fitzpatrick_scale:false,category:"objects"},sleeping_bed:{keywords:["bed","rest"],char:'🛌',fitzpatrick_scale:true,category:"objects"},bed:{keywords:["sleep","rest"],char:'🛏',fitzpatrick_scale:false,category:"objects"},door:{keywords:["house","entry","exit"],char:'🚪',fitzpatrick_scale:false,category:"objects"},bellhop_bell:{keywords:["service"],char:'🛎',fitzpatrick_scale:false,category:"objects"},teddy_bear:{keywords:["plush","stuffed"],char:'🧸',fitzpatrick_scale:false,category:"objects"},framed_picture:{keywords:["photography"],char:'🖼',fitzpatrick_scale:false,category:"objects"},world_map:{keywords:["location","direction"],char:'🗺',fitzpatrick_scale:false,category:"objects"},parasol_on_ground:{keywords:["weather","summer"],char:'⛱',fitzpatrick_scale:false,category:"objects"},moyai:{keywords:["rock","easter island","moai"],char:'🗿',fitzpatrick_scale:false,category:"objects"},shopping:{keywords:["mall","buy","purchase"],char:'🛍',fitzpatrick_scale:false,category:"objects"},shopping_cart:{keywords:["trolley"],char:'🛒',fitzpatrick_scale:false,category:"objects"},balloon:{keywords:["party","celebration","birthday","circus"],char:'🎈',fitzpatrick_scale:false,category:"objects"},flags:{keywords:["fish","japanese","koinobori","carp","banner"],char:'🎏',fitzpatrick_scale:false,category:"objects"},ribbon:{keywords:["decoration","pink","girl","bowtie"],char:'🎀',fitzpatrick_scale:false,category:"objects"},gift:{keywords:["present","birthday","christmas","xmas"],char:'🎁',fitzpatrick_scale:false,category:"objects"},confetti_ball:{keywords:["festival","party","birthday","circus"],char:'🎊',fitzpatrick_scale:false,category:"objects"},tada:{keywords:["party","congratulations","birthday","magic","circus","celebration"],char:'🎉',fitzpatrick_scale:false,category:"objects"},dolls:{keywords:["japanese","toy","kimono"],char:'🎎',fitzpatrick_scale:false,category:"objects"},wind_chime:{keywords:["nature","ding","spring","bell"],char:'🎐',fitzpatrick_scale:false,category:"objects"},crossed_flags:{keywords:["japanese","nation","country","border"],char:'🎌',fitzpatrick_scale:false,category:"objects"},izakaya_lantern:{keywords:["light","paper","halloween","spooky"],char:'🏮',fitzpatrick_scale:false,category:"objects"},red_envelope:{keywords:["gift"],char:'🧧',fitzpatrick_scale:false,category:"objects"},email:{keywords:["letter","postal","inbox","communication"],char:'✉️',fitzpatrick_scale:false,category:"objects"},envelope_with_arrow:{keywords:["email","communication"],char:'📩',fitzpatrick_scale:false,category:"objects"},incoming_envelope:{keywords:["email","inbox"],char:'📨',fitzpatrick_scale:false,category:"objects"},"e-mail":{keywords:["communication","inbox"],char:'📧',fitzpatrick_scale:false,category:"objects"},love_letter:{keywords:["email","like","affection","envelope","valentines"],char:'💌',fitzpatrick_scale:false,category:"objects"},postbox:{keywords:["email","letter","envelope"],char:'📮',fitzpatrick_scale:false,category:"objects"},mailbox_closed:{keywords:["email","communication","inbox"],char:'📪',fitzpatrick_scale:false,category:"objects"},mailbox:{keywords:["email","inbox","communication"],char:'📫',fitzpatrick_scale:false,category:"objects"},mailbox_with_mail:{keywords:["email","inbox","communication"],char:'📬',fitzpatrick_scale:false,category:"objects"},mailbox_with_no_mail:{keywords:["email","inbox"],char:'📭',fitzpatrick_scale:false,category:"objects"},package:{keywords:["mail","gift","cardboard","box","moving"],char:'📦',fitzpatrick_scale:false,category:"objects"},postal_horn:{keywords:["instrument","music"],char:'📯',fitzpatrick_scale:false,category:"objects"},inbox_tray:{keywords:["email","documents"],char:'📥',fitzpatrick_scale:false,category:"objects"},outbox_tray:{keywords:["inbox","email"],char:'📤',fitzpatrick_scale:false,category:"objects"},scroll:{keywords:["documents","ancient","history","paper"],char:'📜',fitzpatrick_scale:false,category:"objects"},page_with_curl:{keywords:["documents","office","paper"],char:'📃',fitzpatrick_scale:false,category:"objects"},bookmark_tabs:{keywords:["favorite","save","order","tidy"],char:'📑',fitzpatrick_scale:false,category:"objects"},receipt:{keywords:["accounting","expenses"],char:'🧾',fitzpatrick_scale:false,category:"objects"},bar_chart:{keywords:["graph","presentation","stats"],char:'📊',fitzpatrick_scale:false,category:"objects"},chart_with_upwards_trend:{keywords:["graph","presentation","stats","recovery","business","economics","money","sales","good","success"],char:'📈',fitzpatrick_scale:false,category:"objects"},chart_with_downwards_trend:{keywords:["graph","presentation","stats","recession","business","economics","money","sales","bad","failure"],char:'📉',fitzpatrick_scale:false,category:"objects"},page_facing_up:{keywords:["documents","office","paper","information"],char:'📄',fitzpatrick_scale:false,category:"objects"},date:{keywords:["calendar","schedule"],char:'📅',fitzpatrick_scale:false,category:"objects"},calendar:{keywords:["schedule","date","planning"],char:'📆',fitzpatrick_scale:false,category:"objects"},spiral_calendar:{keywords:["date","schedule","planning"],char:'🗓',fitzpatrick_scale:false,category:"objects"},card_index:{keywords:["business","stationery"],char:'📇',fitzpatrick_scale:false,category:"objects"},card_file_box:{keywords:["business","stationery"],char:'🗃',fitzpatrick_scale:false,category:"objects"},ballot_box:{keywords:["election","vote"],char:'🗳',fitzpatrick_scale:false,category:"objects"},file_cabinet:{keywords:["filing","organizing"],char:'🗄',fitzpatrick_scale:false,category:"objects"},clipboard:{keywords:["stationery","documents"],char:'📋',fitzpatrick_scale:false,category:"objects"},spiral_notepad:{keywords:["memo","stationery"],char:'🗒',fitzpatrick_scale:false,category:"objects"},file_folder:{keywords:["documents","business","office"],char:'📁',fitzpatrick_scale:false,category:"objects"},open_file_folder:{keywords:["documents","load"],char:'📂',fitzpatrick_scale:false,category:"objects"},card_index_dividers:{keywords:["organizing","business","stationery"],char:'🗂',fitzpatrick_scale:false,category:"objects"},newspaper_roll:{keywords:["press","headline"],char:'🗞',fitzpatrick_scale:false,category:"objects"},newspaper:{keywords:["press","headline"],char:'📰',fitzpatrick_scale:false,category:"objects"},notebook:{keywords:["stationery","record","notes","paper","study"],char:'📓',fitzpatrick_scale:false,category:"objects"},closed_book:{keywords:["read","library","knowledge","textbook","learn"],char:'📕',fitzpatrick_scale:false,category:"objects"},green_book:{keywords:["read","library","knowledge","study"],char:'📗',fitzpatrick_scale:false,category:"objects"},blue_book:{keywords:["read","library","knowledge","learn","study"],char:'📘',fitzpatrick_scale:false,category:"objects"},orange_book:{keywords:["read","library","knowledge","textbook","study"],char:'📙',fitzpatrick_scale:false,category:"objects"},notebook_with_decorative_cover:{keywords:["classroom","notes","record","paper","study"],char:'📔',fitzpatrick_scale:false,category:"objects"},ledger:{keywords:["notes","paper"],char:'📒',fitzpatrick_scale:false,category:"objects"},books:{keywords:["literature","library","study"],char:'📚',fitzpatrick_scale:false,category:"objects"},open_book:{keywords:["book","read","library","knowledge","literature","learn","study"],char:'📖',fitzpatrick_scale:false,category:"objects"},safety_pin:{keywords:["diaper"],char:'🧷',fitzpatrick_scale:false,category:"objects"},link:{keywords:["rings","url"],char:'🔗',fitzpatrick_scale:false,category:"objects"},paperclip:{keywords:["documents","stationery"],char:'📎',fitzpatrick_scale:false,category:"objects"},paperclips:{keywords:["documents","stationery"],char:'🖇',fitzpatrick_scale:false,category:"objects"},scissors:{keywords:["stationery","cut"],char:'✂️',fitzpatrick_scale:false,category:"objects"},triangular_ruler:{keywords:["stationery","math","architect","sketch"],char:'📐',fitzpatrick_scale:false,category:"objects"},straight_ruler:{keywords:["stationery","calculate","length","math","school","drawing","architect","sketch"],char:'📏',fitzpatrick_scale:false,category:"objects"},abacus:{keywords:["calculation"],char:'🧮',fitzpatrick_scale:false,category:"objects"},pushpin:{keywords:["stationery","mark","here"],char:'📌',fitzpatrick_scale:false,category:"objects"},round_pushpin:{keywords:["stationery","location","map","here"],char:'📍',fitzpatrick_scale:false,category:"objects"},triangular_flag_on_post:{keywords:["mark","milestone","place"],char:'🚩',fitzpatrick_scale:false,category:"objects"},white_flag:{keywords:["losing","loser","lost","surrender","give up","fail"],char:'🏳',fitzpatrick_scale:false,category:"objects"},black_flag:{keywords:["pirate"],char:'🏴',fitzpatrick_scale:false,category:"objects"},rainbow_flag:{keywords:["flag","rainbow","pride","gay","lgbt","glbt","queer","homosexual","lesbian","bisexual","transgender"],char:'🏳️‍🌈',fitzpatrick_scale:false,category:"objects"},closed_lock_with_key:{keywords:["security","privacy"],char:'🔐',fitzpatrick_scale:false,category:"objects"},lock:{keywords:["security","password","padlock"],char:'🔒',fitzpatrick_scale:false,category:"objects"},unlock:{keywords:["privacy","security"],char:'🔓',fitzpatrick_scale:false,category:"objects"},lock_with_ink_pen:{keywords:["security","secret"],char:'🔏',fitzpatrick_scale:false,category:"objects"},pen:{keywords:["stationery","writing","write"],char:'🖊',fitzpatrick_scale:false,category:"objects"},fountain_pen:{keywords:["stationery","writing","write"],char:'🖋',fitzpatrick_scale:false,category:"objects"},black_nib:{keywords:["pen","stationery","writing","write"],char:'✒️',fitzpatrick_scale:false,category:"objects"},memo:{keywords:["write","documents","stationery","pencil","paper","writing","legal","exam","quiz","test","study","compose"],char:'📝',fitzpatrick_scale:false,category:"objects"},pencil2:{keywords:["stationery","write","paper","writing","school","study"],char:'✏️',fitzpatrick_scale:false,category:"objects"},crayon:{keywords:["drawing","creativity"],char:'🖍',fitzpatrick_scale:false,category:"objects"},paintbrush:{keywords:["drawing","creativity","art"],char:'🖌',fitzpatrick_scale:false,category:"objects"},mag:{keywords:["search","zoom","find","detective"],char:'🔍',fitzpatrick_scale:false,category:"objects"},mag_right:{keywords:["search","zoom","find","detective"],char:'🔎',fitzpatrick_scale:false,category:"objects"},heart:{keywords:["love","like","valentines"],char:'❤️',fitzpatrick_scale:false,category:"symbols"},orange_heart:{keywords:["love","like","affection","valentines"],char:'🧡',fitzpatrick_scale:false,category:"symbols"},yellow_heart:{keywords:["love","like","affection","valentines"],char:'💛',fitzpatrick_scale:false,category:"symbols"},green_heart:{keywords:["love","like","affection","valentines"],char:'💚',fitzpatrick_scale:false,category:"symbols"},blue_heart:{keywords:["love","like","affection","valentines"],char:'💙',fitzpatrick_scale:false,category:"symbols"},purple_heart:{keywords:["love","like","affection","valentines"],char:'💜',fitzpatrick_scale:false,category:"symbols"},black_heart:{keywords:["evil"],char:'🖤',fitzpatrick_scale:false,category:"symbols"},broken_heart:{keywords:["sad","sorry","break","heart","heartbreak"],char:'💔',fitzpatrick_scale:false,category:"symbols"},heavy_heart_exclamation:{keywords:["decoration","love"],char:'❣',fitzpatrick_scale:false,category:"symbols"},two_hearts:{keywords:["love","like","affection","valentines","heart"],char:'💕',fitzpatrick_scale:false,category:"symbols"},revolving_hearts:{keywords:["love","like","affection","valentines"],char:'💞',fitzpatrick_scale:false,category:"symbols"},heartbeat:{keywords:["love","like","affection","valentines","pink","heart"],char:'💓',fitzpatrick_scale:false,category:"symbols"},heartpulse:{keywords:["like","love","affection","valentines","pink"],char:'💗',fitzpatrick_scale:false,category:"symbols"},sparkling_heart:{keywords:["love","like","affection","valentines"],char:'💖',fitzpatrick_scale:false,category:"symbols"},cupid:{keywords:["love","like","heart","affection","valentines"],char:'💘',fitzpatrick_scale:false,category:"symbols"},gift_heart:{keywords:["love","valentines"],char:'💝',fitzpatrick_scale:false,category:"symbols"},heart_decoration:{keywords:["purple-square","love","like"],char:'💟',fitzpatrick_scale:false,category:"symbols"},peace_symbol:{keywords:["hippie"],char:'☮',fitzpatrick_scale:false,category:"symbols"},latin_cross:{keywords:["christianity"],char:'✝',fitzpatrick_scale:false,category:"symbols"},star_and_crescent:{keywords:["islam"],char:'☪',fitzpatrick_scale:false,category:"symbols"},om:{keywords:["hinduism","buddhism","sikhism","jainism"],char:'🕉',fitzpatrick_scale:false,category:"symbols"},wheel_of_dharma:{keywords:["hinduism","buddhism","sikhism","jainism"],char:'☸',fitzpatrick_scale:false,category:"symbols"},star_of_david:{keywords:["judaism"],char:'✡',fitzpatrick_scale:false,category:"symbols"},six_pointed_star:{keywords:["purple-square","religion","jewish","hexagram"],char:'🔯',fitzpatrick_scale:false,category:"symbols"},menorah:{keywords:["hanukkah","candles","jewish"],char:'🕎',fitzpatrick_scale:false,category:"symbols"},yin_yang:{keywords:["balance"],char:'☯',fitzpatrick_scale:false,category:"symbols"},orthodox_cross:{keywords:["suppedaneum","religion"],char:'☦',fitzpatrick_scale:false,category:"symbols"},place_of_worship:{keywords:["religion","church","temple","prayer"],char:'🛐',fitzpatrick_scale:false,category:"symbols"},ophiuchus:{keywords:["sign","purple-square","constellation","astrology"],char:'⛎',fitzpatrick_scale:false,category:"symbols"},aries:{keywords:["sign","purple-square","zodiac","astrology"],char:'♈',fitzpatrick_scale:false,category:"symbols"},taurus:{keywords:["purple-square","sign","zodiac","astrology"],char:'♉',fitzpatrick_scale:false,category:"symbols"},gemini:{keywords:["sign","zodiac","purple-square","astrology"],char:'♊',fitzpatrick_scale:false,category:"symbols"},cancer:{keywords:["sign","zodiac","purple-square","astrology"],char:'♋',fitzpatrick_scale:false,category:"symbols"},leo:{keywords:["sign","purple-square","zodiac","astrology"],char:'♌',fitzpatrick_scale:false,category:"symbols"},virgo:{keywords:["sign","zodiac","purple-square","astrology"],char:'♍',fitzpatrick_scale:false,category:"symbols"},libra:{keywords:["sign","purple-square","zodiac","astrology"],char:'♎',fitzpatrick_scale:false,category:"symbols"},scorpius:{keywords:["sign","zodiac","purple-square","astrology","scorpio"],char:'♏',fitzpatrick_scale:false,category:"symbols"},sagittarius:{keywords:["sign","zodiac","purple-square","astrology"],char:'♐',fitzpatrick_scale:false,category:"symbols"},capricorn:{keywords:["sign","zodiac","purple-square","astrology"],char:'♑',fitzpatrick_scale:false,category:"symbols"},aquarius:{keywords:["sign","purple-square","zodiac","astrology"],char:'♒',fitzpatrick_scale:false,category:"symbols"},pisces:{keywords:["purple-square","sign","zodiac","astrology"],char:'♓',fitzpatrick_scale:false,category:"symbols"},id:{keywords:["purple-square","words"],char:'🆔',fitzpatrick_scale:false,category:"symbols"},atom_symbol:{keywords:["science","physics","chemistry"],char:'⚛',fitzpatrick_scale:false,category:"symbols"},u7a7a:{keywords:["kanji","japanese","chinese","empty","sky","blue-square"],char:'🈳',fitzpatrick_scale:false,category:"symbols"},u5272:{keywords:["cut","divide","chinese","kanji","pink-square"],char:'🈹',fitzpatrick_scale:false,category:"symbols"},radioactive:{keywords:["nuclear","danger"],char:'☢',fitzpatrick_scale:false,category:"symbols"},biohazard:{keywords:["danger"],char:'☣',fitzpatrick_scale:false,category:"symbols"},mobile_phone_off:{keywords:["mute","orange-square","silence","quiet"],char:'📴',fitzpatrick_scale:false,category:"symbols"},vibration_mode:{keywords:["orange-square","phone"],char:'📳',fitzpatrick_scale:false,category:"symbols"},u6709:{keywords:["orange-square","chinese","have","kanji"],char:'🈶',fitzpatrick_scale:false,category:"symbols"},u7121:{keywords:["nothing","chinese","kanji","japanese","orange-square"],char:'🈚',fitzpatrick_scale:false,category:"symbols"},u7533:{keywords:["chinese","japanese","kanji","orange-square"],char:'🈸',fitzpatrick_scale:false,category:"symbols"},u55b6:{keywords:["japanese","opening hours","orange-square"],char:'🈺',fitzpatrick_scale:false,category:"symbols"},u6708:{keywords:["chinese","month","moon","japanese","orange-square","kanji"],char:'🈷️',fitzpatrick_scale:false,category:"symbols"},eight_pointed_black_star:{keywords:["orange-square","shape","polygon"],char:'✴️',fitzpatrick_scale:false,category:"symbols"},vs:{keywords:["words","orange-square"],char:'🆚',fitzpatrick_scale:false,category:"symbols"},accept:{keywords:["ok","good","chinese","kanji","agree","yes","orange-circle"],char:'🉑',fitzpatrick_scale:false,category:"symbols"},white_flower:{keywords:["japanese","spring"],char:'💮',fitzpatrick_scale:false,category:"symbols"},ideograph_advantage:{keywords:["chinese","kanji","obtain","get","circle"],char:'🉐',fitzpatrick_scale:false,category:"symbols"},secret:{keywords:["privacy","chinese","sshh","kanji","red-circle"],char:'㊙️',fitzpatrick_scale:false,category:"symbols"},congratulations:{keywords:["chinese","kanji","japanese","red-circle"],char:'㊗️',fitzpatrick_scale:false,category:"symbols"},u5408:{keywords:["japanese","chinese","join","kanji","red-square"],char:'🈴',fitzpatrick_scale:false,category:"symbols"},u6e80:{keywords:["full","chinese","japanese","red-square","kanji"],char:'🈵',fitzpatrick_scale:false,category:"symbols"},u7981:{keywords:["kanji","japanese","chinese","forbidden","limit","restricted","red-square"],char:'🈲',fitzpatrick_scale:false,category:"symbols"},a:{keywords:["red-square","alphabet","letter"],char:'🅰️',fitzpatrick_scale:false,category:"symbols"},b:{keywords:["red-square","alphabet","letter"],char:'🅱️',fitzpatrick_scale:false,category:"symbols"},ab:{keywords:["red-square","alphabet"],char:'🆎',fitzpatrick_scale:false,category:"symbols"},cl:{keywords:["alphabet","words","red-square"],char:'🆑',fitzpatrick_scale:false,category:"symbols"},o2:{keywords:["alphabet","red-square","letter"],char:'🅾️',fitzpatrick_scale:false,category:"symbols"},sos:{keywords:["help","red-square","words","emergency","911"],char:'🆘',fitzpatrick_scale:false,category:"symbols"},no_entry:{keywords:["limit","security","privacy","bad","denied","stop","circle"],char:'⛔',fitzpatrick_scale:false,category:"symbols"},name_badge:{keywords:["fire","forbid"],char:'📛',fitzpatrick_scale:false,category:"symbols"},no_entry_sign:{keywords:["forbid","stop","limit","denied","disallow","circle"],char:'🚫',fitzpatrick_scale:false,category:"symbols"},x:{keywords:["no","delete","remove","cancel","red"],char:'❌',fitzpatrick_scale:false,category:"symbols"},o:{keywords:["circle","round"],char:'⭕',fitzpatrick_scale:false,category:"symbols"},stop_sign:{keywords:["stop"],char:'🛑',fitzpatrick_scale:false,category:"symbols"},anger:{keywords:["angry","mad"],char:'💢',fitzpatrick_scale:false,category:"symbols"},hotsprings:{keywords:["bath","warm","relax"],char:'♨️',fitzpatrick_scale:false,category:"symbols"},no_pedestrians:{keywords:["rules","crossing","walking","circle"],char:'🚷',fitzpatrick_scale:false,category:"symbols"},do_not_litter:{keywords:["trash","bin","garbage","circle"],char:'🚯',fitzpatrick_scale:false,category:"symbols"},no_bicycles:{keywords:["cyclist","prohibited","circle"],char:'🚳',fitzpatrick_scale:false,category:"symbols"},"non-potable_water":{keywords:["drink","faucet","tap","circle"],char:'🚱',fitzpatrick_scale:false,category:"symbols"},underage:{keywords:["18","drink","pub","night","minor","circle"],char:'🔞',fitzpatrick_scale:false,category:"symbols"},no_mobile_phones:{keywords:["iphone","mute","circle"],char:'📵',fitzpatrick_scale:false,category:"symbols"},exclamation:{keywords:["heavy_exclamation_mark","danger","surprise","punctuation","wow","warning"],char:'❗',fitzpatrick_scale:false,category:"symbols"},grey_exclamation:{keywords:["surprise","punctuation","gray","wow","warning"],char:'❕',fitzpatrick_scale:false,category:"symbols"},question:{keywords:["doubt","confused"],char:'❓',fitzpatrick_scale:false,category:"symbols"},grey_question:{keywords:["doubts","gray","huh","confused"],char:'❔',fitzpatrick_scale:false,category:"symbols"},bangbang:{keywords:["exclamation","surprise"],char:'‼️',fitzpatrick_scale:false,category:"symbols"},interrobang:{keywords:["wat","punctuation","surprise"],char:'⁉️',fitzpatrick_scale:false,category:"symbols"},low_brightness:{keywords:["sun","afternoon","warm","summer"],char:'🔅',fitzpatrick_scale:false,category:"symbols"},high_brightness:{keywords:["sun","light"],char:'🔆',fitzpatrick_scale:false,category:"symbols"},trident:{keywords:["weapon","spear"],char:'🔱',fitzpatrick_scale:false,category:"symbols"},fleur_de_lis:{keywords:["decorative","scout"],char:'⚜',fitzpatrick_scale:false,category:"symbols"},part_alternation_mark:{keywords:["graph","presentation","stats","business","economics","bad"],char:'〽️',fitzpatrick_scale:false,category:"symbols"},warning:{keywords:["exclamation","wip","alert","error","problem","issue"],char:'⚠️',fitzpatrick_scale:false,category:"symbols"},children_crossing:{keywords:["school","warning","danger","sign","driving","yellow-diamond"],char:'🚸',fitzpatrick_scale:false,category:"symbols"},beginner:{keywords:["badge","shield"],char:'🔰',fitzpatrick_scale:false,category:"symbols"},recycle:{keywords:["arrow","environment","garbage","trash"],char:'♻️',fitzpatrick_scale:false,category:"symbols"},u6307:{keywords:["chinese","point","green-square","kanji"],char:'🈯',fitzpatrick_scale:false,category:"symbols"},chart:{keywords:["green-square","graph","presentation","stats"],char:'💹',fitzpatrick_scale:false,category:"symbols"},sparkle:{keywords:["stars","green-square","awesome","good","fireworks"],char:'❇️',fitzpatrick_scale:false,category:"symbols"},eight_spoked_asterisk:{keywords:["star","sparkle","green-square"],char:'✳️',fitzpatrick_scale:false,category:"symbols"},negative_squared_cross_mark:{keywords:["x","green-square","no","deny"],char:'❎',fitzpatrick_scale:false,category:"symbols"},white_check_mark:{keywords:["green-square","ok","agree","vote","election","answer","tick"],char:'✅',fitzpatrick_scale:false,category:"symbols"},diamond_shape_with_a_dot_inside:{keywords:["jewel","blue","gem","crystal","fancy"],char:'💠',fitzpatrick_scale:false,category:"symbols"},cyclone:{keywords:["weather","swirl","blue","cloud","vortex","spiral","whirlpool","spin","tornado","hurricane","typhoon"],char:'🌀',fitzpatrick_scale:false,category:"symbols"},loop:{keywords:["tape","cassette"],char:'➿',fitzpatrick_scale:false,category:"symbols"},globe_with_meridians:{keywords:["earth","international","world","internet","interweb","i18n"],char:'🌐',fitzpatrick_scale:false,category:"symbols"},m:{keywords:["alphabet","blue-circle","letter"],char:'Ⓜ️',fitzpatrick_scale:false,category:"symbols"},atm:{keywords:["money","sales","cash","blue-square","payment","bank"],char:'🏧',fitzpatrick_scale:false,category:"symbols"},sa:{keywords:["japanese","blue-square","katakana"],char:'🈂️',fitzpatrick_scale:false,category:"symbols"},passport_control:{keywords:["custom","blue-square"],char:'🛂',fitzpatrick_scale:false,category:"symbols"},customs:{keywords:["passport","border","blue-square"],char:'🛃',fitzpatrick_scale:false,category:"symbols"},baggage_claim:{keywords:["blue-square","airport","transport"],char:'🛄',fitzpatrick_scale:false,category:"symbols"},left_luggage:{keywords:["blue-square","travel"],char:'🛅',fitzpatrick_scale:false,category:"symbols"},wheelchair:{keywords:["blue-square","disabled","a11y","accessibility"],char:'♿',fitzpatrick_scale:false,category:"symbols"},no_smoking:{keywords:["cigarette","blue-square","smell","smoke"],char:'🚭',fitzpatrick_scale:false,category:"symbols"},wc:{keywords:["toilet","restroom","blue-square"],char:'🚾',fitzpatrick_scale:false,category:"symbols"},parking:{keywords:["cars","blue-square","alphabet","letter"],char:'🅿️',fitzpatrick_scale:false,category:"symbols"},potable_water:{keywords:["blue-square","liquid","restroom","cleaning","faucet"],char:'🚰',fitzpatrick_scale:false,category:"symbols"},mens:{keywords:["toilet","restroom","wc","blue-square","gender","male"],char:'🚹',fitzpatrick_scale:false,category:"symbols"},womens:{keywords:["purple-square","woman","female","toilet","loo","restroom","gender"],char:'🚺',fitzpatrick_scale:false,category:"symbols"},baby_symbol:{keywords:["orange-square","child"],char:'🚼',fitzpatrick_scale:false,category:"symbols"},restroom:{keywords:["blue-square","toilet","refresh","wc","gender"],char:'🚻',fitzpatrick_scale:false,category:"symbols"},put_litter_in_its_place:{keywords:["blue-square","sign","human","info"],char:'🚮',fitzpatrick_scale:false,category:"symbols"},cinema:{keywords:["blue-square","record","film","movie","curtain","stage","theater"],char:'🎦',fitzpatrick_scale:false,category:"symbols"},signal_strength:{keywords:["blue-square","reception","phone","internet","connection","wifi","bluetooth","bars"],char:'📶',fitzpatrick_scale:false,category:"symbols"},koko:{keywords:["blue-square","here","katakana","japanese","destination"],char:'🈁',fitzpatrick_scale:false,category:"symbols"},ng:{keywords:["blue-square","words","shape","icon"],char:'🆖',fitzpatrick_scale:false,category:"symbols"},ok:{keywords:["good","agree","yes","blue-square"],char:'🆗',fitzpatrick_scale:false,category:"symbols"},up:{keywords:["blue-square","above","high"],char:'🆙',fitzpatrick_scale:false,category:"symbols"},cool:{keywords:["words","blue-square"],char:'🆒',fitzpatrick_scale:false,category:"symbols"},new:{keywords:["blue-square","words","start"],char:'🆕',fitzpatrick_scale:false,category:"symbols"},free:{keywords:["blue-square","words"],char:'🆓',fitzpatrick_scale:false,category:"symbols"},zero:{keywords:["0","numbers","blue-square","null"],char:'0️⃣',fitzpatrick_scale:false,category:"symbols"},one:{keywords:["blue-square","numbers","1"],char:'1️⃣',fitzpatrick_scale:false,category:"symbols"},two:{keywords:["numbers","2","prime","blue-square"],char:'2️⃣',fitzpatrick_scale:false,category:"symbols"},three:{keywords:["3","numbers","prime","blue-square"],char:'3️⃣',fitzpatrick_scale:false,category:"symbols"},four:{keywords:["4","numbers","blue-square"],char:'4️⃣',fitzpatrick_scale:false,category:"symbols"},five:{keywords:["5","numbers","blue-square","prime"],char:'5️⃣',fitzpatrick_scale:false,category:"symbols"},six:{keywords:["6","numbers","blue-square"],char:'6️⃣',fitzpatrick_scale:false,category:"symbols"},seven:{keywords:["7","numbers","blue-square","prime"],char:'7️⃣',fitzpatrick_scale:false,category:"symbols"},eight:{keywords:["8","blue-square","numbers"],char:'8️⃣',fitzpatrick_scale:false,category:"symbols"},nine:{keywords:["blue-square","numbers","9"],char:'9️⃣',fitzpatrick_scale:false,category:"symbols"},keycap_ten:{keywords:["numbers","10","blue-square"],char:'🔟',fitzpatrick_scale:false,category:"symbols"},asterisk:{keywords:["star","keycap"],char:'*⃣',fitzpatrick_scale:false,category:"symbols"},eject_button:{keywords:["blue-square"],char:'⏏️',fitzpatrick_scale:false,category:"symbols"},arrow_forward:{keywords:["blue-square","right","direction","play"],char:'▶️',fitzpatrick_scale:false,category:"symbols"},pause_button:{keywords:["pause","blue-square"],char:'⏸',fitzpatrick_scale:false,category:"symbols"},next_track_button:{keywords:["forward","next","blue-square"],char:'⏭',fitzpatrick_scale:false,category:"symbols"},stop_button:{keywords:["blue-square"],char:'⏹',fitzpatrick_scale:false,category:"symbols"},record_button:{keywords:["blue-square"],char:'⏺',fitzpatrick_scale:false,category:"symbols"},play_or_pause_button:{keywords:["blue-square","play","pause"],char:'⏯',fitzpatrick_scale:false,category:"symbols"},previous_track_button:{keywords:["backward"],char:'⏮',fitzpatrick_scale:false,category:"symbols"},fast_forward:{keywords:["blue-square","play","speed","continue"],char:'⏩',fitzpatrick_scale:false,category:"symbols"},rewind:{keywords:["play","blue-square"],char:'⏪',fitzpatrick_scale:false,category:"symbols"},twisted_rightwards_arrows:{keywords:["blue-square","shuffle","music","random"],char:'🔀',fitzpatrick_scale:false,category:"symbols"},repeat:{keywords:["loop","record"],char:'🔁',fitzpatrick_scale:false,category:"symbols"},repeat_one:{keywords:["blue-square","loop"],char:'🔂',fitzpatrick_scale:false,category:"symbols"},arrow_backward:{keywords:["blue-square","left","direction"],char:'◀️',fitzpatrick_scale:false,category:"symbols"},arrow_up_small:{keywords:["blue-square","triangle","direction","point","forward","top"],char:'🔼',fitzpatrick_scale:false,category:"symbols"},arrow_down_small:{keywords:["blue-square","direction","bottom"],char:'🔽',fitzpatrick_scale:false,category:"symbols"},arrow_double_up:{keywords:["blue-square","direction","top"],char:'⏫',fitzpatrick_scale:false,category:"symbols"},arrow_double_down:{keywords:["blue-square","direction","bottom"],char:'⏬',fitzpatrick_scale:false,category:"symbols"},arrow_right:{keywords:["blue-square","next"],char:'➡️',fitzpatrick_scale:false,category:"symbols"},arrow_left:{keywords:["blue-square","previous","back"],char:'⬅️',fitzpatrick_scale:false,category:"symbols"},arrow_up:{keywords:["blue-square","continue","top","direction"],char:'⬆️',fitzpatrick_scale:false,category:"symbols"},arrow_down:{keywords:["blue-square","direction","bottom"],char:'⬇️',fitzpatrick_scale:false,category:"symbols"},arrow_upper_right:{keywords:["blue-square","point","direction","diagonal","northeast"],char:'↗️',fitzpatrick_scale:false,category:"symbols"},arrow_lower_right:{keywords:["blue-square","direction","diagonal","southeast"],char:'↘️',fitzpatrick_scale:false,category:"symbols"},arrow_lower_left:{keywords:["blue-square","direction","diagonal","southwest"],char:'↙️',fitzpatrick_scale:false,category:"symbols"},arrow_upper_left:{keywords:["blue-square","point","direction","diagonal","northwest"],char:'↖️',fitzpatrick_scale:false,category:"symbols"},arrow_up_down:{keywords:["blue-square","direction","way","vertical"],char:'↕️',fitzpatrick_scale:false,category:"symbols"},left_right_arrow:{keywords:["shape","direction","horizontal","sideways"],char:'↔️',fitzpatrick_scale:false,category:"symbols"},arrows_counterclockwise:{keywords:["blue-square","sync","cycle"],char:'🔄',fitzpatrick_scale:false,category:"symbols"},arrow_right_hook:{keywords:["blue-square","return","rotate","direction"],char:'↪️',fitzpatrick_scale:false,category:"symbols"},leftwards_arrow_with_hook:{keywords:["back","return","blue-square","undo","enter"],char:'↩️',fitzpatrick_scale:false,category:"symbols"},arrow_heading_up:{keywords:["blue-square","direction","top"],char:'⤴️',fitzpatrick_scale:false,category:"symbols"},arrow_heading_down:{keywords:["blue-square","direction","bottom"],char:'⤵️',fitzpatrick_scale:false,category:"symbols"},hash:{keywords:["symbol","blue-square","twitter"],char:'#️⃣',fitzpatrick_scale:false,category:"symbols"},information_source:{keywords:["blue-square","alphabet","letter"],char:'ℹ️',fitzpatrick_scale:false,category:"symbols"},abc:{keywords:["blue-square","alphabet"],char:'🔤',fitzpatrick_scale:false,category:"symbols"},abcd:{keywords:["blue-square","alphabet"],char:'🔡',fitzpatrick_scale:false,category:"symbols"},capital_abcd:{keywords:["alphabet","words","blue-square"],char:'🔠',fitzpatrick_scale:false,category:"symbols"},symbols:{keywords:["blue-square","music","note","ampersand","percent","glyphs","characters"],char:'🔣',fitzpatrick_scale:false,category:"symbols"},musical_note:{keywords:["score","tone","sound"],char:'🎵',fitzpatrick_scale:false,category:"symbols"},notes:{keywords:["music","score"],char:'🎶',fitzpatrick_scale:false,category:"symbols"},wavy_dash:{keywords:["draw","line","moustache","mustache","squiggle","scribble"],char:'〰️',fitzpatrick_scale:false,category:"symbols"},curly_loop:{keywords:["scribble","draw","shape","squiggle"],char:'➰',fitzpatrick_scale:false,category:"symbols"},heavy_check_mark:{keywords:["ok","nike","answer","yes","tick"],char:'✔️',fitzpatrick_scale:false,category:"symbols"},arrows_clockwise:{keywords:["sync","cycle","round","repeat"],char:'🔃',fitzpatrick_scale:false,category:"symbols"},heavy_plus_sign:{keywords:["math","calculation","addition","more","increase"],char:'➕',fitzpatrick_scale:false,category:"symbols"},heavy_minus_sign:{keywords:["math","calculation","subtract","less"],char:'➖',fitzpatrick_scale:false,category:"symbols"},heavy_division_sign:{keywords:["divide","math","calculation"],char:'➗',fitzpatrick_scale:false,category:"symbols"},heavy_multiplication_x:{keywords:["math","calculation"],char:'✖️',fitzpatrick_scale:false,category:"symbols"},infinity:{keywords:["forever"],char:'♾',fitzpatrick_scale:false,category:"symbols"},heavy_dollar_sign:{keywords:["money","sales","payment","currency","buck"],char:'💲',fitzpatrick_scale:false,category:"symbols"},currency_exchange:{keywords:["money","sales","dollar","travel"],char:'💱',fitzpatrick_scale:false,category:"symbols"},copyright:{keywords:["ip","license","circle","law","legal"],char:'©️',fitzpatrick_scale:false,category:"symbols"},registered:{keywords:["alphabet","circle"],char:'®️',fitzpatrick_scale:false,category:"symbols"},tm:{keywords:["trademark","brand","law","legal"],char:'™️',fitzpatrick_scale:false,category:"symbols"},end:{keywords:["words","arrow"],char:'🔚',fitzpatrick_scale:false,category:"symbols"},back:{keywords:["arrow","words","return"],char:'🔙',fitzpatrick_scale:false,category:"symbols"},on:{keywords:["arrow","words"],char:'🔛',fitzpatrick_scale:false,category:"symbols"},top:{keywords:["words","blue-square"],char:'🔝',fitzpatrick_scale:false,category:"symbols"},soon:{keywords:["arrow","words"],char:'🔜',fitzpatrick_scale:false,category:"symbols"},ballot_box_with_check:{keywords:["ok","agree","confirm","black-square","vote","election","yes","tick"],char:'☑️',fitzpatrick_scale:false,category:"symbols"},radio_button:{keywords:["input","old","music","circle"],char:'🔘',fitzpatrick_scale:false,category:"symbols"},white_circle:{keywords:["shape","round"],char:'⚪',fitzpatrick_scale:false,category:"symbols"},black_circle:{keywords:["shape","button","round"],char:'⚫',fitzpatrick_scale:false,category:"symbols"},red_circle:{keywords:["shape","error","danger"],char:'🔴',fitzpatrick_scale:false,category:"symbols"},large_blue_circle:{keywords:["shape","icon","button"],char:'🔵',fitzpatrick_scale:false,category:"symbols"},small_orange_diamond:{keywords:["shape","jewel","gem"],char:'🔸',fitzpatrick_scale:false,category:"symbols"},small_blue_diamond:{keywords:["shape","jewel","gem"],char:'🔹',fitzpatrick_scale:false,category:"symbols"},large_orange_diamond:{keywords:["shape","jewel","gem"],char:'🔶',fitzpatrick_scale:false,category:"symbols"},large_blue_diamond:{keywords:["shape","jewel","gem"],char:'🔷',fitzpatrick_scale:false,category:"symbols"},small_red_triangle:{keywords:["shape","direction","up","top"],char:'🔺',fitzpatrick_scale:false,category:"symbols"},black_small_square:{keywords:["shape","icon"],char:'▪️',fitzpatrick_scale:false,category:"symbols"},white_small_square:{keywords:["shape","icon"],char:'▫️',fitzpatrick_scale:false,category:"symbols"},black_large_square:{keywords:["shape","icon","button"],char:'⬛',fitzpatrick_scale:false,category:"symbols"},white_large_square:{keywords:["shape","icon","stone","button"],char:'⬜',fitzpatrick_scale:false,category:"symbols"},small_red_triangle_down:{keywords:["shape","direction","bottom"],char:'🔻',fitzpatrick_scale:false,category:"symbols"},black_medium_square:{keywords:["shape","button","icon"],char:'◼️',fitzpatrick_scale:false,category:"symbols"},white_medium_square:{keywords:["shape","stone","icon"],char:'◻️',fitzpatrick_scale:false,category:"symbols"},black_medium_small_square:{keywords:["icon","shape","button"],char:'◾',fitzpatrick_scale:false,category:"symbols"},white_medium_small_square:{keywords:["shape","stone","icon","button"],char:'◽',fitzpatrick_scale:false,category:"symbols"},black_square_button:{keywords:["shape","input","frame"],char:'🔲',fitzpatrick_scale:false,category:"symbols"},white_square_button:{keywords:["shape","input"],char:'🔳',fitzpatrick_scale:false,category:"symbols"},speaker:{keywords:["sound","volume","silence","broadcast"],char:'🔈',fitzpatrick_scale:false,category:"symbols"},sound:{keywords:["volume","speaker","broadcast"],char:'🔉',fitzpatrick_scale:false,category:"symbols"},loud_sound:{keywords:["volume","noise","noisy","speaker","broadcast"],char:'🔊',fitzpatrick_scale:false,category:"symbols"},mute:{keywords:["sound","volume","silence","quiet"],char:'🔇',fitzpatrick_scale:false,category:"symbols"},mega:{keywords:["sound","speaker","volume"],char:'📣',fitzpatrick_scale:false,category:"symbols"},loudspeaker:{keywords:["volume","sound"],char:'📢',fitzpatrick_scale:false,category:"symbols"},bell:{keywords:["sound","notification","christmas","xmas","chime"],char:'🔔',fitzpatrick_scale:false,category:"symbols"},no_bell:{keywords:["sound","volume","mute","quiet","silent"],char:'🔕',fitzpatrick_scale:false,category:"symbols"},black_joker:{keywords:["poker","cards","game","play","magic"],char:'🃏',fitzpatrick_scale:false,category:"symbols"},mahjong:{keywords:["game","play","chinese","kanji"],char:'🀄',fitzpatrick_scale:false,category:"symbols"},spades:{keywords:["poker","cards","suits","magic"],char:'♠️',fitzpatrick_scale:false,category:"symbols"},clubs:{keywords:["poker","cards","magic","suits"],char:'♣️',fitzpatrick_scale:false,category:"symbols"},hearts:{keywords:["poker","cards","magic","suits"],char:'♥️',fitzpatrick_scale:false,category:"symbols"},diamonds:{keywords:["poker","cards","magic","suits"],char:'♦️',fitzpatrick_scale:false,category:"symbols"},flower_playing_cards:{keywords:["game","sunset","red"],char:'🎴',fitzpatrick_scale:false,category:"symbols"},thought_balloon:{keywords:["bubble","cloud","speech","thinking","dream"],char:'💭',fitzpatrick_scale:false,category:"symbols"},right_anger_bubble:{keywords:["caption","speech","thinking","mad"],char:'🗯',fitzpatrick_scale:false,category:"symbols"},speech_balloon:{keywords:["bubble","words","message","talk","chatting"],char:'💬',fitzpatrick_scale:false,category:"symbols"},left_speech_bubble:{keywords:["words","message","talk","chatting"],char:'🗨',fitzpatrick_scale:false,category:"symbols"},clock1:{keywords:["time","late","early","schedule"],char:'🕐',fitzpatrick_scale:false,category:"symbols"},clock2:{keywords:["time","late","early","schedule"],char:'🕑',fitzpatrick_scale:false,category:"symbols"},clock3:{keywords:["time","late","early","schedule"],char:'🕒',fitzpatrick_scale:false,category:"symbols"},clock4:{keywords:["time","late","early","schedule"],char:'🕓',fitzpatrick_scale:false,category:"symbols"},clock5:{keywords:["time","late","early","schedule"],char:'🕔',fitzpatrick_scale:false,category:"symbols"},clock6:{keywords:["time","late","early","schedule","dawn","dusk"],char:'🕕',fitzpatrick_scale:false,category:"symbols"},clock7:{keywords:["time","late","early","schedule"],char:'🕖',fitzpatrick_scale:false,category:"symbols"},clock8:{keywords:["time","late","early","schedule"],char:'🕗',fitzpatrick_scale:false,category:"symbols"},clock9:{keywords:["time","late","early","schedule"],char:'🕘',fitzpatrick_scale:false,category:"symbols"},clock10:{keywords:["time","late","early","schedule"],char:'🕙',fitzpatrick_scale:false,category:"symbols"},clock11:{keywords:["time","late","early","schedule"],char:'🕚',fitzpatrick_scale:false,category:"symbols"},clock12:{keywords:["time","noon","midnight","midday","late","early","schedule"],char:'🕛',fitzpatrick_scale:false,category:"symbols"},clock130:{keywords:["time","late","early","schedule"],char:'🕜',fitzpatrick_scale:false,category:"symbols"},clock230:{keywords:["time","late","early","schedule"],char:'🕝',fitzpatrick_scale:false,category:"symbols"},clock330:{keywords:["time","late","early","schedule"],char:'🕞',fitzpatrick_scale:false,category:"symbols"},clock430:{keywords:["time","late","early","schedule"],char:'🕟',fitzpatrick_scale:false,category:"symbols"},clock530:{keywords:["time","late","early","schedule"],char:'🕠',fitzpatrick_scale:false,category:"symbols"},clock630:{keywords:["time","late","early","schedule"],char:'🕡',fitzpatrick_scale:false,category:"symbols"},clock730:{keywords:["time","late","early","schedule"],char:'🕢',fitzpatrick_scale:false,category:"symbols"},clock830:{keywords:["time","late","early","schedule"],char:'🕣',fitzpatrick_scale:false,category:"symbols"},clock930:{keywords:["time","late","early","schedule"],char:'🕤',fitzpatrick_scale:false,category:"symbols"},clock1030:{keywords:["time","late","early","schedule"],char:'🕥',fitzpatrick_scale:false,category:"symbols"},clock1130:{keywords:["time","late","early","schedule"],char:'🕦',fitzpatrick_scale:false,category:"symbols"},clock1230:{keywords:["time","late","early","schedule"],char:'🕧',fitzpatrick_scale:false,category:"symbols"},afghanistan:{keywords:["af","flag","nation","country","banner"],char:'🇦🇫',fitzpatrick_scale:false,category:"flags"},aland_islands:{keywords:["Åland","islands","flag","nation","country","banner"],char:'🇦🇽',fitzpatrick_scale:false,category:"flags"},albania:{keywords:["al","flag","nation","country","banner"],char:'🇦🇱',fitzpatrick_scale:false,category:"flags"},algeria:{keywords:["dz","flag","nation","country","banner"],char:'🇩🇿',fitzpatrick_scale:false,category:"flags"},american_samoa:{keywords:["american","ws","flag","nation","country","banner"],char:'🇦🇸',fitzpatrick_scale:false,category:"flags"},andorra:{keywords:["ad","flag","nation","country","banner"],char:'🇦🇩',fitzpatrick_scale:false,category:"flags"},angola:{keywords:["ao","flag","nation","country","banner"],char:'🇦🇴',fitzpatrick_scale:false,category:"flags"},anguilla:{keywords:["ai","flag","nation","country","banner"],char:'🇦🇮',fitzpatrick_scale:false,category:"flags"},antarctica:{keywords:["aq","flag","nation","country","banner"],char:'🇦🇶',fitzpatrick_scale:false,category:"flags"},antigua_barbuda:{keywords:["antigua","barbuda","flag","nation","country","banner"],char:'🇦🇬',fitzpatrick_scale:false,category:"flags"},argentina:{keywords:["ar","flag","nation","country","banner"],char:'🇦🇷',fitzpatrick_scale:false,category:"flags"},armenia:{keywords:["am","flag","nation","country","banner"],char:'🇦🇲',fitzpatrick_scale:false,category:"flags"},aruba:{keywords:["aw","flag","nation","country","banner"],char:'🇦🇼',fitzpatrick_scale:false,category:"flags"},australia:{keywords:["au","flag","nation","country","banner"],char:'🇦🇺',fitzpatrick_scale:false,category:"flags"},austria:{keywords:["at","flag","nation","country","banner"],char:'🇦🇹',fitzpatrick_scale:false,category:"flags"},azerbaijan:{keywords:["az","flag","nation","country","banner"],char:'🇦🇿',fitzpatrick_scale:false,category:"flags"},bahamas:{keywords:["bs","flag","nation","country","banner"],char:'🇧🇸',fitzpatrick_scale:false,category:"flags"},bahrain:{keywords:["bh","flag","nation","country","banner"],char:'🇧🇭',fitzpatrick_scale:false,category:"flags"},bangladesh:{keywords:["bd","flag","nation","country","banner"],char:'🇧🇩',fitzpatrick_scale:false,category:"flags"},barbados:{keywords:["bb","flag","nation","country","banner"],char:'🇧🇧',fitzpatrick_scale:false,category:"flags"},belarus:{keywords:["by","flag","nation","country","banner"],char:'🇧🇾',fitzpatrick_scale:false,category:"flags"},belgium:{keywords:["be","flag","nation","country","banner"],char:'🇧🇪',fitzpatrick_scale:false,category:"flags"},belize:{keywords:["bz","flag","nation","country","banner"],char:'🇧🇿',fitzpatrick_scale:false,category:"flags"},benin:{keywords:["bj","flag","nation","country","banner"],char:'🇧🇯',fitzpatrick_scale:false,category:"flags"},bermuda:{keywords:["bm","flag","nation","country","banner"],char:'🇧🇲',fitzpatrick_scale:false,category:"flags"},bhutan:{keywords:["bt","flag","nation","country","banner"],char:'🇧🇹',fitzpatrick_scale:false,category:"flags"},bolivia:{keywords:["bo","flag","nation","country","banner"],char:'🇧🇴',fitzpatrick_scale:false,category:"flags"},caribbean_netherlands:{keywords:["bonaire","flag","nation","country","banner"],char:'🇧🇶',fitzpatrick_scale:false,category:"flags"},bosnia_herzegovina:{keywords:["bosnia","herzegovina","flag","nation","country","banner"],char:'🇧🇦',fitzpatrick_scale:false,category:"flags"},botswana:{keywords:["bw","flag","nation","country","banner"],char:'🇧🇼',fitzpatrick_scale:false,category:"flags"},brazil:{keywords:["br","flag","nation","country","banner"],char:'🇧🇷',fitzpatrick_scale:false,category:"flags"},british_indian_ocean_territory:{keywords:["british","indian","ocean","territory","flag","nation","country","banner"],char:'🇮🇴',fitzpatrick_scale:false,category:"flags"},british_virgin_islands:{keywords:["british","virgin","islands","bvi","flag","nation","country","banner"],char:'🇻🇬',fitzpatrick_scale:false,category:"flags"},brunei:{keywords:["bn","darussalam","flag","nation","country","banner"],char:'🇧🇳',fitzpatrick_scale:false,category:"flags"},bulgaria:{keywords:["bg","flag","nation","country","banner"],char:'🇧🇬',fitzpatrick_scale:false,category:"flags"},burkina_faso:{keywords:["burkina","faso","flag","nation","country","banner"],char:'🇧🇫',fitzpatrick_scale:false,category:"flags"},burundi:{keywords:["bi","flag","nation","country","banner"],char:'🇧🇮',fitzpatrick_scale:false,category:"flags"},cape_verde:{keywords:["cabo","verde","flag","nation","country","banner"],char:'🇨🇻',fitzpatrick_scale:false,category:"flags"},cambodia:{keywords:["kh","flag","nation","country","banner"],char:'🇰🇭',fitzpatrick_scale:false,category:"flags"},cameroon:{keywords:["cm","flag","nation","country","banner"],char:'🇨🇲',fitzpatrick_scale:false,category:"flags"},canada:{keywords:["ca","flag","nation","country","banner"],char:'🇨🇦',fitzpatrick_scale:false,category:"flags"},canary_islands:{keywords:["canary","islands","flag","nation","country","banner"],char:'🇮🇨',fitzpatrick_scale:false,category:"flags"},cayman_islands:{keywords:["cayman","islands","flag","nation","country","banner"],char:'🇰🇾',fitzpatrick_scale:false,category:"flags"},central_african_republic:{keywords:["central","african","republic","flag","nation","country","banner"],char:'🇨🇫',fitzpatrick_scale:false,category:"flags"},chad:{keywords:["td","flag","nation","country","banner"],char:'🇹🇩',fitzpatrick_scale:false,category:"flags"},chile:{keywords:["flag","nation","country","banner"],char:'🇨🇱',fitzpatrick_scale:false,category:"flags"},cn:{keywords:["china","chinese","prc","flag","country","nation","banner"],char:'🇨🇳',fitzpatrick_scale:false,category:"flags"},christmas_island:{keywords:["christmas","island","flag","nation","country","banner"],char:'🇨🇽',fitzpatrick_scale:false,category:"flags"},cocos_islands:{keywords:["cocos","keeling","islands","flag","nation","country","banner"],char:'🇨🇨',fitzpatrick_scale:false,category:"flags"},colombia:{keywords:["co","flag","nation","country","banner"],char:'🇨🇴',fitzpatrick_scale:false,category:"flags"},comoros:{keywords:["km","flag","nation","country","banner"],char:'🇰🇲',fitzpatrick_scale:false,category:"flags"},congo_brazzaville:{keywords:["congo","flag","nation","country","banner"],char:'🇨🇬',fitzpatrick_scale:false,category:"flags"},congo_kinshasa:{keywords:["congo","democratic","republic","flag","nation","country","banner"],char:'🇨🇩',fitzpatrick_scale:false,category:"flags"},cook_islands:{keywords:["cook","islands","flag","nation","country","banner"],char:'🇨🇰',fitzpatrick_scale:false,category:"flags"},costa_rica:{keywords:["costa","rica","flag","nation","country","banner"],char:'🇨🇷',fitzpatrick_scale:false,category:"flags"},croatia:{keywords:["hr","flag","nation","country","banner"],char:'🇭🇷',fitzpatrick_scale:false,category:"flags"},cuba:{keywords:["cu","flag","nation","country","banner"],char:'🇨🇺',fitzpatrick_scale:false,category:"flags"},curacao:{keywords:["curaçao","flag","nation","country","banner"],char:'🇨🇼',fitzpatrick_scale:false,category:"flags"},cyprus:{keywords:["cy","flag","nation","country","banner"],char:'🇨🇾',fitzpatrick_scale:false,category:"flags"},czech_republic:{keywords:["cz","flag","nation","country","banner"],char:'🇨🇿',fitzpatrick_scale:false,category:"flags"},denmark:{keywords:["dk","flag","nation","country","banner"],char:'🇩🇰',fitzpatrick_scale:false,category:"flags"},djibouti:{keywords:["dj","flag","nation","country","banner"],char:'🇩🇯',fitzpatrick_scale:false,category:"flags"},dominica:{keywords:["dm","flag","nation","country","banner"],char:'🇩🇲',fitzpatrick_scale:false,category:"flags"},dominican_republic:{keywords:["dominican","republic","flag","nation","country","banner"],char:'🇩🇴',fitzpatrick_scale:false,category:"flags"},ecuador:{keywords:["ec","flag","nation","country","banner"],char:'🇪🇨',fitzpatrick_scale:false,category:"flags"},egypt:{keywords:["eg","flag","nation","country","banner"],char:'🇪🇬',fitzpatrick_scale:false,category:"flags"},el_salvador:{keywords:["el","salvador","flag","nation","country","banner"],char:'🇸🇻',fitzpatrick_scale:false,category:"flags"},equatorial_guinea:{keywords:["equatorial","gn","flag","nation","country","banner"],char:'🇬🇶',fitzpatrick_scale:false,category:"flags"},eritrea:{keywords:["er","flag","nation","country","banner"],char:'🇪🇷',fitzpatrick_scale:false,category:"flags"},estonia:{keywords:["ee","flag","nation","country","banner"],char:'🇪🇪',fitzpatrick_scale:false,category:"flags"},ethiopia:{keywords:["et","flag","nation","country","banner"],char:'🇪🇹',fitzpatrick_scale:false,category:"flags"},eu:{keywords:["european","union","flag","banner"],char:'🇪🇺',fitzpatrick_scale:false,category:"flags"},falkland_islands:{keywords:["falkland","islands","malvinas","flag","nation","country","banner"],char:'🇫🇰',fitzpatrick_scale:false,category:"flags"},faroe_islands:{keywords:["faroe","islands","flag","nation","country","banner"],char:'🇫🇴',fitzpatrick_scale:false,category:"flags"},fiji:{keywords:["fj","flag","nation","country","banner"],char:'🇫🇯',fitzpatrick_scale:false,category:"flags"},finland:{keywords:["fi","flag","nation","country","banner"],char:'🇫🇮',fitzpatrick_scale:false,category:"flags"},fr:{keywords:["banner","flag","nation","france","french","country"],char:'🇫🇷',fitzpatrick_scale:false,category:"flags"},french_guiana:{keywords:["french","guiana","flag","nation","country","banner"],char:'🇬🇫',fitzpatrick_scale:false,category:"flags"},french_polynesia:{keywords:["french","polynesia","flag","nation","country","banner"],char:'🇵🇫',fitzpatrick_scale:false,category:"flags"},french_southern_territories:{keywords:["french","southern","territories","flag","nation","country","banner"],char:'🇹🇫',fitzpatrick_scale:false,category:"flags"},gabon:{keywords:["ga","flag","nation","country","banner"],char:'🇬🇦',fitzpatrick_scale:false,category:"flags"},gambia:{keywords:["gm","flag","nation","country","banner"],char:'🇬🇲',fitzpatrick_scale:false,category:"flags"},georgia:{keywords:["ge","flag","nation","country","banner"],char:'🇬🇪',fitzpatrick_scale:false,category:"flags"},de:{keywords:["german","nation","flag","country","banner"],char:'🇩🇪',fitzpatrick_scale:false,category:"flags"},ghana:{keywords:["gh","flag","nation","country","banner"],char:'🇬🇭',fitzpatrick_scale:false,category:"flags"},gibraltar:{keywords:["gi","flag","nation","country","banner"],char:'🇬🇮',fitzpatrick_scale:false,category:"flags"},greece:{keywords:["gr","flag","nation","country","banner"],char:'🇬🇷',fitzpatrick_scale:false,category:"flags"},greenland:{keywords:["gl","flag","nation","country","banner"],char:'🇬🇱',fitzpatrick_scale:false,category:"flags"},grenada:{keywords:["gd","flag","nation","country","banner"],char:'🇬🇩',fitzpatrick_scale:false,category:"flags"},guadeloupe:{keywords:["gp","flag","nation","country","banner"],char:'🇬🇵',fitzpatrick_scale:false,category:"flags"},guam:{keywords:["gu","flag","nation","country","banner"],char:'🇬🇺',fitzpatrick_scale:false,category:"flags"},guatemala:{keywords:["gt","flag","nation","country","banner"],char:'🇬🇹',fitzpatrick_scale:false,category:"flags"},guernsey:{keywords:["gg","flag","nation","country","banner"],char:'🇬🇬',fitzpatrick_scale:false,category:"flags"},guinea:{keywords:["gn","flag","nation","country","banner"],char:'🇬🇳',fitzpatrick_scale:false,category:"flags"},guinea_bissau:{keywords:["gw","bissau","flag","nation","country","banner"],char:'🇬🇼',fitzpatrick_scale:false,category:"flags"},guyana:{keywords:["gy","flag","nation","country","banner"],char:'🇬🇾',fitzpatrick_scale:false,category:"flags"},haiti:{keywords:["ht","flag","nation","country","banner"],char:'🇭🇹',fitzpatrick_scale:false,category:"flags"},honduras:{keywords:["hn","flag","nation","country","banner"],char:'🇭🇳',fitzpatrick_scale:false,category:"flags"},hong_kong:{keywords:["hong","kong","flag","nation","country","banner"],char:'🇭🇰',fitzpatrick_scale:false,category:"flags"},hungary:{keywords:["hu","flag","nation","country","banner"],char:'🇭🇺',fitzpatrick_scale:false,category:"flags"},iceland:{keywords:["is","flag","nation","country","banner"],char:'🇮🇸',fitzpatrick_scale:false,category:"flags"},india:{keywords:["in","flag","nation","country","banner"],char:'🇮🇳',fitzpatrick_scale:false,category:"flags"},indonesia:{keywords:["flag","nation","country","banner"],char:'🇮🇩',fitzpatrick_scale:false,category:"flags"},iran:{keywords:["iran,","islamic","republic","flag","nation","country","banner"],char:'🇮🇷',fitzpatrick_scale:false,category:"flags"},iraq:{keywords:["iq","flag","nation","country","banner"],char:'🇮🇶',fitzpatrick_scale:false,category:"flags"},ireland:{keywords:["ie","flag","nation","country","banner"],char:'🇮🇪',fitzpatrick_scale:false,category:"flags"},isle_of_man:{keywords:["isle","man","flag","nation","country","banner"],char:'🇮🇲',fitzpatrick_scale:false,category:"flags"},israel:{keywords:["il","flag","nation","country","banner"],char:'🇮🇱',fitzpatrick_scale:false,category:"flags"},it:{keywords:["italy","flag","nation","country","banner"],char:'🇮🇹',fitzpatrick_scale:false,category:"flags"},cote_divoire:{keywords:["ivory","coast","flag","nation","country","banner"],char:'🇨🇮',fitzpatrick_scale:false,category:"flags"},jamaica:{keywords:["jm","flag","nation","country","banner"],char:'🇯🇲',fitzpatrick_scale:false,category:"flags"},jp:{keywords:["japanese","nation","flag","country","banner"],char:'🇯🇵',fitzpatrick_scale:false,category:"flags"},jersey:{keywords:["je","flag","nation","country","banner"],char:'🇯🇪',fitzpatrick_scale:false,category:"flags"},jordan:{keywords:["jo","flag","nation","country","banner"],char:'🇯🇴',fitzpatrick_scale:false,category:"flags"},kazakhstan:{keywords:["kz","flag","nation","country","banner"],char:'🇰🇿',fitzpatrick_scale:false,category:"flags"},kenya:{keywords:["ke","flag","nation","country","banner"],char:'🇰🇪',fitzpatrick_scale:false,category:"flags"},kiribati:{keywords:["ki","flag","nation","country","banner"],char:'🇰🇮',fitzpatrick_scale:false,category:"flags"},kosovo:{keywords:["xk","flag","nation","country","banner"],char:'🇽🇰',fitzpatrick_scale:false,category:"flags"},kuwait:{keywords:["kw","flag","nation","country","banner"],char:'🇰🇼',fitzpatrick_scale:false,category:"flags"},kyrgyzstan:{keywords:["kg","flag","nation","country","banner"],char:'🇰🇬',fitzpatrick_scale:false,category:"flags"},laos:{keywords:["lao","democratic","republic","flag","nation","country","banner"],char:'🇱🇦',fitzpatrick_scale:false,category:"flags"},latvia:{keywords:["lv","flag","nation","country","banner"],char:'🇱🇻',fitzpatrick_scale:false,category:"flags"},lebanon:{keywords:["lb","flag","nation","country","banner"],char:'🇱🇧',fitzpatrick_scale:false,category:"flags"},lesotho:{keywords:["ls","flag","nation","country","banner"],char:'🇱🇸',fitzpatrick_scale:false,category:"flags"},liberia:{keywords:["lr","flag","nation","country","banner"],char:'🇱🇷',fitzpatrick_scale:false,category:"flags"},libya:{keywords:["ly","flag","nation","country","banner"],char:'🇱🇾',fitzpatrick_scale:false,category:"flags"},liechtenstein:{keywords:["li","flag","nation","country","banner"],char:'🇱🇮',fitzpatrick_scale:false,category:"flags"},lithuania:{keywords:["lt","flag","nation","country","banner"],char:'🇱🇹',fitzpatrick_scale:false,category:"flags"},luxembourg:{keywords:["lu","flag","nation","country","banner"],char:'🇱🇺',fitzpatrick_scale:false,category:"flags"},macau:{keywords:["macao","flag","nation","country","banner"],char:'🇲🇴',fitzpatrick_scale:false,category:"flags"},macedonia:{keywords:["macedonia,","flag","nation","country","banner"],char:'🇲🇰',fitzpatrick_scale:false,category:"flags"},madagascar:{keywords:["mg","flag","nation","country","banner"],char:'🇲🇬',fitzpatrick_scale:false,category:"flags"},malawi:{keywords:["mw","flag","nation","country","banner"],char:'🇲🇼',fitzpatrick_scale:false,category:"flags"},malaysia:{keywords:["my","flag","nation","country","banner"],char:'🇲🇾',fitzpatrick_scale:false,category:"flags"},maldives:{keywords:["mv","flag","nation","country","banner"],char:'🇲🇻',fitzpatrick_scale:false,category:"flags"},mali:{keywords:["ml","flag","nation","country","banner"],char:'🇲🇱',fitzpatrick_scale:false,category:"flags"},malta:{keywords:["mt","flag","nation","country","banner"],char:'🇲🇹',fitzpatrick_scale:false,category:"flags"},marshall_islands:{keywords:["marshall","islands","flag","nation","country","banner"],char:'🇲🇭',fitzpatrick_scale:false,category:"flags"},martinique:{keywords:["mq","flag","nation","country","banner"],char:'🇲🇶',fitzpatrick_scale:false,category:"flags"},mauritania:{keywords:["mr","flag","nation","country","banner"],char:'🇲🇷',fitzpatrick_scale:false,category:"flags"},mauritius:{keywords:["mu","flag","nation","country","banner"],char:'🇲🇺',fitzpatrick_scale:false,category:"flags"},mayotte:{keywords:["yt","flag","nation","country","banner"],char:'🇾🇹',fitzpatrick_scale:false,category:"flags"},mexico:{keywords:["mx","flag","nation","country","banner"],char:'🇲🇽',fitzpatrick_scale:false,category:"flags"},micronesia:{keywords:["micronesia,","federated","states","flag","nation","country","banner"],char:'🇫🇲',fitzpatrick_scale:false,category:"flags"},moldova:{keywords:["moldova,","republic","flag","nation","country","banner"],char:'🇲🇩',fitzpatrick_scale:false,category:"flags"},monaco:{keywords:["mc","flag","nation","country","banner"],char:'🇲🇨',fitzpatrick_scale:false,category:"flags"},mongolia:{keywords:["mn","flag","nation","country","banner"],char:'🇲🇳',fitzpatrick_scale:false,category:"flags"},montenegro:{keywords:["me","flag","nation","country","banner"],char:'🇲🇪',fitzpatrick_scale:false,category:"flags"},montserrat:{keywords:["ms","flag","nation","country","banner"],char:'🇲🇸',fitzpatrick_scale:false,category:"flags"},morocco:{keywords:["ma","flag","nation","country","banner"],char:'🇲🇦',fitzpatrick_scale:false,category:"flags"},mozambique:{keywords:["mz","flag","nation","country","banner"],char:'🇲🇿',fitzpatrick_scale:false,category:"flags"},myanmar:{keywords:["mm","flag","nation","country","banner"],char:'🇲🇲',fitzpatrick_scale:false,category:"flags"},namibia:{keywords:["na","flag","nation","country","banner"],char:'🇳🇦',fitzpatrick_scale:false,category:"flags"},nauru:{keywords:["nr","flag","nation","country","banner"],char:'🇳🇷',fitzpatrick_scale:false,category:"flags"},nepal:{keywords:["np","flag","nation","country","banner"],char:'🇳🇵',fitzpatrick_scale:false,category:"flags"},netherlands:{keywords:["nl","flag","nation","country","banner"],char:'🇳🇱',fitzpatrick_scale:false,category:"flags"},new_caledonia:{keywords:["new","caledonia","flag","nation","country","banner"],char:'🇳🇨',fitzpatrick_scale:false,category:"flags"},new_zealand:{keywords:["new","zealand","flag","nation","country","banner"],char:'🇳🇿',fitzpatrick_scale:false,category:"flags"},nicaragua:{keywords:["ni","flag","nation","country","banner"],char:'🇳🇮',fitzpatrick_scale:false,category:"flags"},niger:{keywords:["ne","flag","nation","country","banner"],char:'🇳🇪',fitzpatrick_scale:false,category:"flags"},nigeria:{keywords:["flag","nation","country","banner"],char:'🇳🇬',fitzpatrick_scale:false,category:"flags"},niue:{keywords:["nu","flag","nation","country","banner"],char:'🇳🇺',fitzpatrick_scale:false,category:"flags"},norfolk_island:{keywords:["norfolk","island","flag","nation","country","banner"],char:'🇳🇫',fitzpatrick_scale:false,category:"flags"},northern_mariana_islands:{keywords:["northern","mariana","islands","flag","nation","country","banner"],char:'🇲🇵',fitzpatrick_scale:false,category:"flags"},north_korea:{keywords:["north","korea","nation","flag","country","banner"],char:'🇰🇵',fitzpatrick_scale:false,category:"flags"},norway:{keywords:["no","flag","nation","country","banner"],char:'🇳🇴',fitzpatrick_scale:false,category:"flags"},oman:{keywords:["om_symbol","flag","nation","country","banner"],char:'🇴🇲',fitzpatrick_scale:false,category:"flags"},pakistan:{keywords:["pk","flag","nation","country","banner"],char:'🇵🇰',fitzpatrick_scale:false,category:"flags"},palau:{keywords:["pw","flag","nation","country","banner"],char:'🇵🇼',fitzpatrick_scale:false,category:"flags"},palestinian_territories:{keywords:["palestine","palestinian","territories","flag","nation","country","banner"],char:'🇵🇸',fitzpatrick_scale:false,category:"flags"},panama:{keywords:["pa","flag","nation","country","banner"],char:'🇵🇦',fitzpatrick_scale:false,category:"flags"},papua_new_guinea:{keywords:["papua","new","guinea","flag","nation","country","banner"],char:'🇵🇬',fitzpatrick_scale:false,category:"flags"},paraguay:{keywords:["py","flag","nation","country","banner"],char:'🇵🇾',fitzpatrick_scale:false,category:"flags"},peru:{keywords:["pe","flag","nation","country","banner"],char:'🇵🇪',fitzpatrick_scale:false,category:"flags"},philippines:{keywords:["ph","flag","nation","country","banner"],char:'🇵🇭',fitzpatrick_scale:false,category:"flags"},pitcairn_islands:{keywords:["pitcairn","flag","nation","country","banner"],char:'🇵🇳',fitzpatrick_scale:false,category:"flags"},poland:{keywords:["pl","flag","nation","country","banner"],char:'🇵🇱',fitzpatrick_scale:false,category:"flags"},portugal:{keywords:["pt","flag","nation","country","banner"],char:'🇵🇹',fitzpatrick_scale:false,category:"flags"},puerto_rico:{keywords:["puerto","rico","flag","nation","country","banner"],char:'🇵🇷',fitzpatrick_scale:false,category:"flags"},qatar:{keywords:["qa","flag","nation","country","banner"],char:'🇶🇦',fitzpatrick_scale:false,category:"flags"},reunion:{keywords:["réunion","flag","nation","country","banner"],char:'🇷🇪',fitzpatrick_scale:false,category:"flags"},romania:{keywords:["ro","flag","nation","country","banner"],char:'🇷🇴',fitzpatrick_scale:false,category:"flags"},ru:{keywords:["russian","federation","flag","nation","country","banner"],char:'🇷🇺',fitzpatrick_scale:false,category:"flags"},rwanda:{keywords:["rw","flag","nation","country","banner"],char:'🇷🇼',fitzpatrick_scale:false,category:"flags"},st_barthelemy:{keywords:["saint","barthélemy","flag","nation","country","banner"],char:'🇧🇱',fitzpatrick_scale:false,category:"flags"},st_helena:{keywords:["saint","helena","ascension","tristan","cunha","flag","nation","country","banner"],char:'🇸🇭',fitzpatrick_scale:false,category:"flags"},st_kitts_nevis:{keywords:["saint","kitts","nevis","flag","nation","country","banner"],char:'🇰🇳',fitzpatrick_scale:false,category:"flags"},st_lucia:{keywords:["saint","lucia","flag","nation","country","banner"],char:'🇱🇨',fitzpatrick_scale:false,category:"flags"},st_pierre_miquelon:{keywords:["saint","pierre","miquelon","flag","nation","country","banner"],char:'🇵🇲',fitzpatrick_scale:false,category:"flags"},st_vincent_grenadines:{keywords:["saint","vincent","grenadines","flag","nation","country","banner"],char:'🇻🇨',fitzpatrick_scale:false,category:"flags"},samoa:{keywords:["ws","flag","nation","country","banner"],char:'🇼🇸',fitzpatrick_scale:false,category:"flags"},san_marino:{keywords:["san","marino","flag","nation","country","banner"],char:'🇸🇲',fitzpatrick_scale:false,category:"flags"},sao_tome_principe:{keywords:["sao","tome","principe","flag","nation","country","banner"],char:'🇸🇹',fitzpatrick_scale:false,category:"flags"},saudi_arabia:{keywords:["flag","nation","country","banner"],char:'🇸🇦',fitzpatrick_scale:false,category:"flags"},senegal:{keywords:["sn","flag","nation","country","banner"],char:'🇸🇳',fitzpatrick_scale:false,category:"flags"},serbia:{keywords:["rs","flag","nation","country","banner"],char:'🇷🇸',fitzpatrick_scale:false,category:"flags"},seychelles:{keywords:["sc","flag","nation","country","banner"],char:'🇸🇨',fitzpatrick_scale:false,category:"flags"},sierra_leone:{keywords:["sierra","leone","flag","nation","country","banner"],char:'🇸🇱',fitzpatrick_scale:false,category:"flags"},singapore:{keywords:["sg","flag","nation","country","banner"],char:'🇸🇬',fitzpatrick_scale:false,category:"flags"},sint_maarten:{keywords:["sint","maarten","dutch","flag","nation","country","banner"],char:'🇸🇽',fitzpatrick_scale:false,category:"flags"},slovakia:{keywords:["sk","flag","nation","country","banner"],char:'🇸🇰',fitzpatrick_scale:false,category:"flags"},slovenia:{keywords:["si","flag","nation","country","banner"],char:'🇸🇮',fitzpatrick_scale:false,category:"flags"},solomon_islands:{keywords:["solomon","islands","flag","nation","country","banner"],char:'🇸🇧',fitzpatrick_scale:false,category:"flags"},somalia:{keywords:["so","flag","nation","country","banner"],char:'🇸🇴',fitzpatrick_scale:false,category:"flags"},south_africa:{keywords:["south","africa","flag","nation","country","banner"],char:'🇿🇦',fitzpatrick_scale:false,category:"flags"},south_georgia_south_sandwich_islands:{keywords:["south","georgia","sandwich","islands","flag","nation","country","banner"],char:'🇬🇸',fitzpatrick_scale:false,category:"flags"},kr:{keywords:["south","korea","nation","flag","country","banner"],char:'🇰🇷',fitzpatrick_scale:false,category:"flags"},south_sudan:{keywords:["south","sd","flag","nation","country","banner"],char:'🇸🇸',fitzpatrick_scale:false,category:"flags"},es:{keywords:["spain","flag","nation","country","banner"],char:'🇪🇸',fitzpatrick_scale:false,category:"flags"},sri_lanka:{keywords:["sri","lanka","flag","nation","country","banner"],char:'🇱🇰',fitzpatrick_scale:false,category:"flags"},sudan:{keywords:["sd","flag","nation","country","banner"],char:'🇸🇩',fitzpatrick_scale:false,category:"flags"},suriname:{keywords:["sr","flag","nation","country","banner"],char:'🇸🇷',fitzpatrick_scale:false,category:"flags"},swaziland:{keywords:["sz","flag","nation","country","banner"],char:'🇸🇿',fitzpatrick_scale:false,category:"flags"},sweden:{keywords:["se","flag","nation","country","banner"],char:'🇸🇪',fitzpatrick_scale:false,category:"flags"},switzerland:{keywords:["ch","flag","nation","country","banner"],char:'🇨🇭',fitzpatrick_scale:false,category:"flags"},syria:{keywords:["syrian","arab","republic","flag","nation","country","banner"],char:'🇸🇾',fitzpatrick_scale:false,category:"flags"},taiwan:{keywords:["tw","flag","nation","country","banner"],char:'🇹🇼',fitzpatrick_scale:false,category:"flags"},tajikistan:{keywords:["tj","flag","nation","country","banner"],char:'🇹🇯',fitzpatrick_scale:false,category:"flags"},tanzania:{keywords:["tanzania,","united","republic","flag","nation","country","banner"],char:'🇹🇿',fitzpatrick_scale:false,category:"flags"},thailand:{keywords:["th","flag","nation","country","banner"],char:'🇹🇭',fitzpatrick_scale:false,category:"flags"},timor_leste:{keywords:["timor","leste","flag","nation","country","banner"],char:'🇹🇱',fitzpatrick_scale:false,category:"flags"},togo:{keywords:["tg","flag","nation","country","banner"],char:'🇹🇬',fitzpatrick_scale:false,category:"flags"},tokelau:{keywords:["tk","flag","nation","country","banner"],char:'🇹🇰',fitzpatrick_scale:false,category:"flags"},tonga:{keywords:["to","flag","nation","country","banner"],char:'🇹🇴',fitzpatrick_scale:false,category:"flags"},trinidad_tobago:{keywords:["trinidad","tobago","flag","nation","country","banner"],char:'🇹🇹',fitzpatrick_scale:false,category:"flags"},tunisia:{keywords:["tn","flag","nation","country","banner"],char:'🇹🇳',fitzpatrick_scale:false,category:"flags"},tr:{keywords:["turkey","flag","nation","country","banner"],char:'🇹🇷',fitzpatrick_scale:false,category:"flags"},turkmenistan:{keywords:["flag","nation","country","banner"],char:'🇹🇲',fitzpatrick_scale:false,category:"flags"},turks_caicos_islands:{keywords:["turks","caicos","islands","flag","nation","country","banner"],char:'🇹🇨',fitzpatrick_scale:false,category:"flags"},tuvalu:{keywords:["flag","nation","country","banner"],char:'🇹🇻',fitzpatrick_scale:false,category:"flags"},uganda:{keywords:["ug","flag","nation","country","banner"],char:'🇺🇬',fitzpatrick_scale:false,category:"flags"},ukraine:{keywords:["ua","flag","nation","country","banner"],char:'🇺🇦',fitzpatrick_scale:false,category:"flags"},united_arab_emirates:{keywords:["united","arab","emirates","flag","nation","country","banner"],char:'🇦🇪',fitzpatrick_scale:false,category:"flags"},uk:{keywords:["united","kingdom","great","britain","northern","ireland","flag","nation","country","banner","british","UK","english","england","union jack"],char:'🇬🇧',fitzpatrick_scale:false,category:"flags"},england:{keywords:["flag","english"],char:'🏴󠁧󠁢󠁥󠁮󠁧󠁿',fitzpatrick_scale:false,category:"flags"},scotland:{keywords:["flag","scottish"],char:'🏴󠁧󠁢󠁳󠁣󠁴󠁿',fitzpatrick_scale:false,category:"flags"},wales:{keywords:["flag","welsh"],char:'🏴󠁧󠁢󠁷󠁬󠁳󠁿',fitzpatrick_scale:false,category:"flags"},us:{keywords:["united","states","america","flag","nation","country","banner"],char:'🇺🇸',fitzpatrick_scale:false,category:"flags"},us_virgin_islands:{keywords:["virgin","islands","us","flag","nation","country","banner"],char:'🇻🇮',fitzpatrick_scale:false,category:"flags"},uruguay:{keywords:["uy","flag","nation","country","banner"],char:'🇺🇾',fitzpatrick_scale:false,category:"flags"},uzbekistan:{keywords:["uz","flag","nation","country","banner"],char:'🇺🇿',fitzpatrick_scale:false,category:"flags"},vanuatu:{keywords:["vu","flag","nation","country","banner"],char:'🇻🇺',fitzpatrick_scale:false,category:"flags"},vatican_city:{keywords:["vatican","city","flag","nation","country","banner"],char:'🇻🇦',fitzpatrick_scale:false,category:"flags"},venezuela:{keywords:["ve","bolivarian","republic","flag","nation","country","banner"],char:'🇻🇪',fitzpatrick_scale:false,category:"flags"},vietnam:{keywords:["viet","nam","flag","nation","country","banner"],char:'🇻🇳',fitzpatrick_scale:false,category:"flags"},wallis_futuna:{keywords:["wallis","futuna","flag","nation","country","banner"],char:'🇼🇫',fitzpatrick_scale:false,category:"flags"},western_sahara:{keywords:["western","sahara","flag","nation","country","banner"],char:'🇪🇭',fitzpatrick_scale:false,category:"flags"},yemen:{keywords:["ye","flag","nation","country","banner"],char:'🇾🇪',fitzpatrick_scale:false,category:"flags"},zambia:{keywords:["zm","flag","nation","country","banner"],char:'🇿🇲',fitzpatrick_scale:false,category:"flags"},zimbabwe:{keywords:["zw","flag","nation","country","banner"],char:'🇿🇼',fitzpatrick_scale:false,category:"flags"},united_nations:{keywords:["un","flag","banner"],char:'🇺🇳',fitzpatrick_scale:false,category:"flags"},pirate_flag:{keywords:["skull","crossbones","flag","banner"],char:'🏴‍☠️',fitzpatrick_scale:false,category:"flags"}}); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/emoticons/js/emojiimages.min.js b/apps/web-antd/public/tinymce/plugins/emoticons/js/emojiimages.min.js new file mode 100644 index 0000000..37f3bcf --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/emoticons/js/emojiimages.min.js @@ -0,0 +1,3 @@ +// Source: npm package: emojilib +// Images provided by twemoji: https://github.com/twitter/twemoji +window.tinymce.Resource.add("tinymce.plugins.emoticons",{100:{keywords:["score","perfect","numbers","century","exam","quiz","test","pass","hundred"],char:'\u{1f4af}',fitzpatrick_scale:!1,category:"symbols"},1234:{keywords:["numbers","blue-square"],char:'\u{1f522}',fitzpatrick_scale:!1,category:"symbols"},grinning:{keywords:["face","smile","happy","joy",":D","grin"],char:'\u{1f600}',fitzpatrick_scale:!1,category:"people"},grimacing:{keywords:["face","grimace","teeth"],char:'\u{1f62c}',fitzpatrick_scale:!1,category:"people"},grin:{keywords:["face","happy","smile","joy","kawaii"],char:'\u{1f601}',fitzpatrick_scale:!1,category:"people"},joy:{keywords:["face","cry","tears","weep","happy","happytears","haha"],char:'\u{1f602}',fitzpatrick_scale:!1,category:"people"},rofl:{keywords:["face","rolling","floor","laughing","lol","haha"],char:'\u{1f923}',fitzpatrick_scale:!1,category:"people"},partying:{keywords:["face","celebration","woohoo"],char:'\u{1f973}',fitzpatrick_scale:!1,category:"people"},smiley:{keywords:["face","happy","joy","haha",":D",":)","smile","funny"],char:'\u{1f603}',fitzpatrick_scale:!1,category:"people"},smile:{keywords:["face","happy","joy","funny","haha","laugh","like",":D",":)"],char:'\u{1f604}',fitzpatrick_scale:!1,category:"people"},sweat_smile:{keywords:["face","hot","happy","laugh","sweat","smile","relief"],char:'\u{1f605}',fitzpatrick_scale:!1,category:"people"},laughing:{keywords:["happy","joy","lol","satisfied","haha","face","glad","XD","laugh"],char:'\u{1f606}',fitzpatrick_scale:!1,category:"people"},innocent:{keywords:["face","angel","heaven","halo"],char:'\u{1f607}',fitzpatrick_scale:!1,category:"people"},wink:{keywords:["face","happy","mischievous","secret",";)","smile","eye"],char:'\u{1f609}',fitzpatrick_scale:!1,category:"people"},blush:{keywords:["face","smile","happy","flushed","crush","embarrassed","shy","joy"],char:'\u{1f60a}',fitzpatrick_scale:!1,category:"people"},slightly_smiling_face:{keywords:["face","smile"],char:'\u{1f642}',fitzpatrick_scale:!1,category:"people"},upside_down_face:{keywords:["face","flipped","silly","smile"],char:'\u{1f643}',fitzpatrick_scale:!1,category:"people"},relaxed:{keywords:["face","blush","massage","happiness"],char:'\u263a\ufe0f',fitzpatrick_scale:!1,category:"people"},yum:{keywords:["happy","joy","tongue","smile","face","silly","yummy","nom","delicious","savouring"],char:'\u{1f60b}',fitzpatrick_scale:!1,category:"people"},relieved:{keywords:["face","relaxed","phew","massage","happiness"],char:'\u{1f60c}',fitzpatrick_scale:!1,category:"people"},heart_eyes:{keywords:["face","love","like","affection","valentines","infatuation","crush","heart"],char:'\u{1f60d}',fitzpatrick_scale:!1,category:"people"},smiling_face_with_three_hearts:{keywords:["face","love","like","affection","valentines","infatuation","crush","hearts","adore"],char:'\u{1f970}',fitzpatrick_scale:!1,category:"people"},kissing_heart:{keywords:["face","love","like","affection","valentines","infatuation","kiss"],char:'\u{1f618}',fitzpatrick_scale:!1,category:"people"},kissing:{keywords:["love","like","face","3","valentines","infatuation","kiss"],char:'\u{1f617}',fitzpatrick_scale:!1,category:"people"},kissing_smiling_eyes:{keywords:["face","affection","valentines","infatuation","kiss"],char:'\u{1f619}',fitzpatrick_scale:!1,category:"people"},kissing_closed_eyes:{keywords:["face","love","like","affection","valentines","infatuation","kiss"],char:'\u{1f61a}',fitzpatrick_scale:!1,category:"people"},stuck_out_tongue_winking_eye:{keywords:["face","prank","childish","playful","mischievous","smile","wink","tongue"],char:'\u{1f61c}',fitzpatrick_scale:!1,category:"people"},zany:{keywords:["face","goofy","crazy"],char:'\u{1f92a}',fitzpatrick_scale:!1,category:"people"},raised_eyebrow:{keywords:["face","distrust","scepticism","disapproval","disbelief","surprise"],char:'\u{1f928}',fitzpatrick_scale:!1,category:"people"},monocle:{keywords:["face","stuffy","wealthy"],char:'\u{1f9d0}',fitzpatrick_scale:!1,category:"people"},stuck_out_tongue_closed_eyes:{keywords:["face","prank","playful","mischievous","smile","tongue"],char:'\u{1f61d}',fitzpatrick_scale:!1,category:"people"},stuck_out_tongue:{keywords:["face","prank","childish","playful","mischievous","smile","tongue"],char:'\u{1f61b}',fitzpatrick_scale:!1,category:"people"},money_mouth_face:{keywords:["face","rich","dollar","money"],char:'\u{1f911}',fitzpatrick_scale:!1,category:"people"},nerd_face:{keywords:["face","nerdy","geek","dork"],char:'\u{1f913}',fitzpatrick_scale:!1,category:"people"},sunglasses:{keywords:["face","cool","smile","summer","beach","sunglass"],char:'\u{1f60e}',fitzpatrick_scale:!1,category:"people"},star_struck:{keywords:["face","smile","starry","eyes","grinning"],char:'\u{1f929}',fitzpatrick_scale:!1,category:"people"},clown_face:{keywords:["face"],char:'\u{1f921}',fitzpatrick_scale:!1,category:"people"},cowboy_hat_face:{keywords:["face","cowgirl","hat"],char:'\u{1f920}',fitzpatrick_scale:!1,category:"people"},hugs:{keywords:["face","smile","hug"],char:'\u{1f917}',fitzpatrick_scale:!1,category:"people"},smirk:{keywords:["face","smile","mean","prank","smug","sarcasm"],char:'\u{1f60f}',fitzpatrick_scale:!1,category:"people"},no_mouth:{keywords:["face","hellokitty"],char:'\u{1f636}',fitzpatrick_scale:!1,category:"people"},neutral_face:{keywords:["indifference","meh",":|","neutral"],char:'\u{1f610}',fitzpatrick_scale:!1,category:"people"},expressionless:{keywords:["face","indifferent","-_-","meh","deadpan"],char:'\u{1f611}',fitzpatrick_scale:!1,category:"people"},unamused:{keywords:["indifference","bored","straight face","serious","sarcasm","unimpressed","skeptical","dubious","side_eye"],char:'\u{1f612}',fitzpatrick_scale:!1,category:"people"},roll_eyes:{keywords:["face","eyeroll","frustrated"],char:'\u{1f644}',fitzpatrick_scale:!1,category:"people"},thinking:{keywords:["face","hmmm","think","consider"],char:'\u{1f914}',fitzpatrick_scale:!1,category:"people"},lying_face:{keywords:["face","lie","pinocchio"],char:'\u{1f925}',fitzpatrick_scale:!1,category:"people"},hand_over_mouth:{keywords:["face","whoops","shock","surprise"],char:'\u{1f92d}',fitzpatrick_scale:!1,category:"people"},shushing:{keywords:["face","quiet","shhh"],char:'\u{1f92b}',fitzpatrick_scale:!1,category:"people"},symbols_over_mouth:{keywords:["face","swearing","cursing","cussing","profanity","expletive"],char:'\u{1f92c}',fitzpatrick_scale:!1,category:"people"},exploding_head:{keywords:["face","shocked","mind","blown"],char:'\u{1f92f}',fitzpatrick_scale:!1,category:"people"},flushed:{keywords:["face","blush","shy","flattered"],char:'\u{1f633}',fitzpatrick_scale:!1,category:"people"},disappointed:{keywords:["face","sad","upset","depressed",":("],char:'\u{1f61e}',fitzpatrick_scale:!1,category:"people"},worried:{keywords:["face","concern","nervous",":("],char:'\u{1f61f}',fitzpatrick_scale:!1,category:"people"},angry:{keywords:["mad","face","annoyed","frustrated"],char:'\u{1f620}',fitzpatrick_scale:!1,category:"people"},rage:{keywords:["angry","mad","hate","despise"],char:'\u{1f621}',fitzpatrick_scale:!1,category:"people"},pensive:{keywords:["face","sad","depressed","upset"],char:'\u{1f614}',fitzpatrick_scale:!1,category:"people"},confused:{keywords:["face","indifference","huh","weird","hmmm",":/"],char:'\u{1f615}',fitzpatrick_scale:!1,category:"people"},slightly_frowning_face:{keywords:["face","frowning","disappointed","sad","upset"],char:'\u{1f641}',fitzpatrick_scale:!1,category:"people"},frowning_face:{keywords:["face","sad","upset","frown"],char:'\u2639',fitzpatrick_scale:!1,category:"people"},persevere:{keywords:["face","sick","no","upset","oops"],char:'\u{1f623}',fitzpatrick_scale:!1,category:"people"},confounded:{keywords:["face","confused","sick","unwell","oops",":S"],char:'\u{1f616}',fitzpatrick_scale:!1,category:"people"},tired_face:{keywords:["sick","whine","upset","frustrated"],char:'\u{1f62b}',fitzpatrick_scale:!1,category:"people"},weary:{keywords:["face","tired","sleepy","sad","frustrated","upset"],char:'\u{1f629}',fitzpatrick_scale:!1,category:"people"},pleading:{keywords:["face","begging","mercy"],char:'\u{1f97a}',fitzpatrick_scale:!1,category:"people"},triumph:{keywords:["face","gas","phew","proud","pride"],char:'\u{1f624}',fitzpatrick_scale:!1,category:"people"},open_mouth:{keywords:["face","surprise","impressed","wow","whoa",":O"],char:'\u{1f62e}',fitzpatrick_scale:!1,category:"people"},scream:{keywords:["face","munch","scared","omg"],char:'\u{1f631}',fitzpatrick_scale:!1,category:"people"},fearful:{keywords:["face","scared","terrified","nervous","oops","huh"],char:'\u{1f628}',fitzpatrick_scale:!1,category:"people"},cold_sweat:{keywords:["face","nervous","sweat"],char:'\u{1f630}',fitzpatrick_scale:!1,category:"people"},hushed:{keywords:["face","woo","shh"],char:'\u{1f62f}',fitzpatrick_scale:!1,category:"people"},frowning:{keywords:["face","aw","what"],char:'\u{1f626}',fitzpatrick_scale:!1,category:"people"},anguished:{keywords:["face","stunned","nervous"],char:'\u{1f627}',fitzpatrick_scale:!1,category:"people"},cry:{keywords:["face","tears","sad","depressed","upset",":'("],char:'\u{1f622}',fitzpatrick_scale:!1,category:"people"},disappointed_relieved:{keywords:["face","phew","sweat","nervous"],char:'\u{1f625}',fitzpatrick_scale:!1,category:"people"},drooling_face:{keywords:["face"],char:'\u{1f924}',fitzpatrick_scale:!1,category:"people"},sleepy:{keywords:["face","tired","rest","nap"],char:'\u{1f62a}',fitzpatrick_scale:!1,category:"people"},sweat:{keywords:["face","hot","sad","tired","exercise"],char:'\u{1f613}',fitzpatrick_scale:!1,category:"people"},hot:{keywords:["face","feverish","heat","red","sweating"],char:'\u{1f975}',fitzpatrick_scale:!1,category:"people"},cold:{keywords:["face","blue","freezing","frozen","frostbite","icicles"],char:'\u{1f976}',fitzpatrick_scale:!1,category:"people"},sob:{keywords:["face","cry","tears","sad","upset","depressed"],char:'\u{1f62d}',fitzpatrick_scale:!1,category:"people"},dizzy_face:{keywords:["spent","unconscious","xox","dizzy"],char:'\u{1f635}',fitzpatrick_scale:!1,category:"people"},astonished:{keywords:["face","xox","surprised","poisoned"],char:'\u{1f632}',fitzpatrick_scale:!1,category:"people"},zipper_mouth_face:{keywords:["face","sealed","zipper","secret"],char:'\u{1f910}',fitzpatrick_scale:!1,category:"people"},nauseated_face:{keywords:["face","vomit","gross","green","sick","throw up","ill"],char:'\u{1f922}',fitzpatrick_scale:!1,category:"people"},sneezing_face:{keywords:["face","gesundheit","sneeze","sick","allergy"],char:'\u{1f927}',fitzpatrick_scale:!1,category:"people"},vomiting:{keywords:["face","sick"],char:'\u{1f92e}',fitzpatrick_scale:!1,category:"people"},mask:{keywords:["face","sick","ill","disease"],char:'\u{1f637}',fitzpatrick_scale:!1,category:"people"},face_with_thermometer:{keywords:["sick","temperature","thermometer","cold","fever"],char:'\u{1f912}',fitzpatrick_scale:!1,category:"people"},face_with_head_bandage:{keywords:["injured","clumsy","bandage","hurt"],char:'\u{1f915}',fitzpatrick_scale:!1,category:"people"},woozy:{keywords:["face","dizzy","intoxicated","tipsy","wavy"],char:'\u{1f974}',fitzpatrick_scale:!1,category:"people"},sleeping:{keywords:["face","tired","sleepy","night","zzz"],char:'\u{1f634}',fitzpatrick_scale:!1,category:"people"},zzz:{keywords:["sleepy","tired","dream"],char:'\u{1f4a4}',fitzpatrick_scale:!1,category:"people"},poop:{keywords:["hankey","shitface","fail","turd","shit"],char:'\u{1f4a9}',fitzpatrick_scale:!1,category:"people"},smiling_imp:{keywords:["devil","horns"],char:'\u{1f608}',fitzpatrick_scale:!1,category:"people"},imp:{keywords:["devil","angry","horns"],char:'\u{1f47f}',fitzpatrick_scale:!1,category:"people"},japanese_ogre:{keywords:["monster","red","mask","halloween","scary","creepy","devil","demon","japanese","ogre"],char:'\u{1f479}',fitzpatrick_scale:!1,category:"people"},japanese_goblin:{keywords:["red","evil","mask","monster","scary","creepy","japanese","goblin"],char:'\u{1f47a}',fitzpatrick_scale:!1,category:"people"},skull:{keywords:["dead","skeleton","creepy","death"],char:'\u{1f480}',fitzpatrick_scale:!1,category:"people"},ghost:{keywords:["halloween","spooky","scary"],char:'\u{1f47b}',fitzpatrick_scale:!1,category:"people"},alien:{keywords:["UFO","paul","weird","outer_space"],char:'\u{1f47d}',fitzpatrick_scale:!1,category:"people"},robot:{keywords:["computer","machine","bot"],char:'\u{1f916}',fitzpatrick_scale:!1,category:"people"},smiley_cat:{keywords:["animal","cats","happy","smile"],char:'\u{1f63a}',fitzpatrick_scale:!1,category:"people"},smile_cat:{keywords:["animal","cats","smile"],char:'\u{1f638}',fitzpatrick_scale:!1,category:"people"},joy_cat:{keywords:["animal","cats","haha","happy","tears"],char:'\u{1f639}',fitzpatrick_scale:!1,category:"people"},heart_eyes_cat:{keywords:["animal","love","like","affection","cats","valentines","heart"],char:'\u{1f63b}',fitzpatrick_scale:!1,category:"people"},smirk_cat:{keywords:["animal","cats","smirk"],char:'\u{1f63c}',fitzpatrick_scale:!1,category:"people"},kissing_cat:{keywords:["animal","cats","kiss"],char:'\u{1f63d}',fitzpatrick_scale:!1,category:"people"},scream_cat:{keywords:["animal","cats","munch","scared","scream"],char:'\u{1f640}',fitzpatrick_scale:!1,category:"people"},crying_cat_face:{keywords:["animal","tears","weep","sad","cats","upset","cry"],char:'\u{1f63f}',fitzpatrick_scale:!1,category:"people"},pouting_cat:{keywords:["animal","cats"],char:'\u{1f63e}',fitzpatrick_scale:!1,category:"people"},palms_up:{keywords:["hands","gesture","cupped","prayer"],char:'\u{1f932}',fitzpatrick_scale:!0,category:"people"},raised_hands:{keywords:["gesture","hooray","yea","celebration","hands"],char:'\u{1f64c}',fitzpatrick_scale:!0,category:"people"},clap:{keywords:["hands","praise","applause","congrats","yay"],char:'\u{1f44f}',fitzpatrick_scale:!0,category:"people"},wave:{keywords:["hands","gesture","goodbye","solong","farewell","hello","hi","palm"],char:'\u{1f44b}',fitzpatrick_scale:!0,category:"people"},call_me_hand:{keywords:["hands","gesture"],char:'\u{1f919}',fitzpatrick_scale:!0,category:"people"},"+1":{keywords:["thumbsup","yes","awesome","good","agree","accept","cool","hand","like"],char:'\u{1f44d}',fitzpatrick_scale:!0,category:"people"},"-1":{keywords:["thumbsdown","no","dislike","hand"],char:'\u{1f44e}',fitzpatrick_scale:!0,category:"people"},facepunch:{keywords:["angry","violence","fist","hit","attack","hand"],char:'\u{1f44a}',fitzpatrick_scale:!0,category:"people"},fist:{keywords:["fingers","hand","grasp"],char:'\u270a',fitzpatrick_scale:!0,category:"people"},fist_left:{keywords:["hand","fistbump"],char:'\u{1f91b}',fitzpatrick_scale:!0,category:"people"},fist_right:{keywords:["hand","fistbump"],char:'\u{1f91c}',fitzpatrick_scale:!0,category:"people"},v:{keywords:["fingers","ohyeah","hand","peace","victory","two"],char:'\u270c',fitzpatrick_scale:!0,category:"people"},ok_hand:{keywords:["fingers","limbs","perfect","ok","okay"],char:'\u{1f44c}',fitzpatrick_scale:!0,category:"people"},raised_hand:{keywords:["fingers","stop","highfive","palm","ban"],char:'\u270b',fitzpatrick_scale:!0,category:"people"},raised_back_of_hand:{keywords:["fingers","raised","backhand"],char:'\u{1f91a}',fitzpatrick_scale:!0,category:"people"},open_hands:{keywords:["fingers","butterfly","hands","open"],char:'\u{1f450}',fitzpatrick_scale:!0,category:"people"},muscle:{keywords:["arm","flex","hand","summer","strong","biceps"],char:'\u{1f4aa}',fitzpatrick_scale:!0,category:"people"},pray:{keywords:["please","hope","wish","namaste","highfive"],char:'\u{1f64f}',fitzpatrick_scale:!0,category:"people"},foot:{keywords:["kick","stomp"],char:'\u{1f9b6}',fitzpatrick_scale:!0,category:"people"},leg:{keywords:["kick","limb"],char:'\u{1f9b5}',fitzpatrick_scale:!0,category:"people"},handshake:{keywords:["agreement","shake"],char:'\u{1f91d}',fitzpatrick_scale:!1,category:"people"},point_up:{keywords:["hand","fingers","direction","up"],char:'\u261d',fitzpatrick_scale:!0,category:"people"},point_up_2:{keywords:["fingers","hand","direction","up"],char:'\u{1f446}',fitzpatrick_scale:!0,category:"people"},point_down:{keywords:["fingers","hand","direction","down"],char:'\u{1f447}',fitzpatrick_scale:!0,category:"people"},point_left:{keywords:["direction","fingers","hand","left"],char:'\u{1f448}',fitzpatrick_scale:!0,category:"people"},point_right:{keywords:["fingers","hand","direction","right"],char:'\u{1f449}',fitzpatrick_scale:!0,category:"people"},fu:{keywords:["hand","fingers","rude","middle","flipping"],char:'\u{1f595}',fitzpatrick_scale:!0,category:"people"},raised_hand_with_fingers_splayed:{keywords:["hand","fingers","palm"],char:'\u{1f590}',fitzpatrick_scale:!0,category:"people"},love_you:{keywords:["hand","fingers","gesture"],char:'\u{1f91f}',fitzpatrick_scale:!0,category:"people"},metal:{keywords:["hand","fingers","evil_eye","sign_of_horns","rock_on"],char:'\u{1f918}',fitzpatrick_scale:!0,category:"people"},crossed_fingers:{keywords:["good","lucky"],char:'\u{1f91e}',fitzpatrick_scale:!0,category:"people"},vulcan_salute:{keywords:["hand","fingers","spock","star trek"],char:'\u{1f596}',fitzpatrick_scale:!0,category:"people"},writing_hand:{keywords:["lower_left_ballpoint_pen","stationery","write","compose"],char:'\u270d',fitzpatrick_scale:!0,category:"people"},selfie:{keywords:["camera","phone"],char:'\u{1f933}',fitzpatrick_scale:!0,category:"people"},nail_care:{keywords:["beauty","manicure","finger","fashion","nail"],char:'\u{1f485}',fitzpatrick_scale:!0,category:"people"},lips:{keywords:["mouth","kiss"],char:'\u{1f444}',fitzpatrick_scale:!1,category:"people"},tooth:{keywords:["teeth","dentist"],char:'\u{1f9b7}',fitzpatrick_scale:!1,category:"people"},tongue:{keywords:["mouth","playful"],char:'\u{1f445}',fitzpatrick_scale:!1,category:"people"},ear:{keywords:["face","hear","sound","listen"],char:'\u{1f442}',fitzpatrick_scale:!0,category:"people"},nose:{keywords:["smell","sniff"],char:'\u{1f443}',fitzpatrick_scale:!0,category:"people"},eye:{keywords:["face","look","see","watch","stare"],char:'\u{1f441}',fitzpatrick_scale:!1,category:"people"},eyes:{keywords:["look","watch","stalk","peek","see"],char:'\u{1f440}',fitzpatrick_scale:!1,category:"people"},brain:{keywords:["smart","intelligent"],char:'\u{1f9e0}',fitzpatrick_scale:!1,category:"people"},bust_in_silhouette:{keywords:["user","person","human"],char:'\u{1f464}',fitzpatrick_scale:!1,category:"people"},busts_in_silhouette:{keywords:["user","person","human","group","team"],char:'\u{1f465}',fitzpatrick_scale:!1,category:"people"},speaking_head:{keywords:["user","person","human","sing","say","talk"],char:'\u{1f5e3}',fitzpatrick_scale:!1,category:"people"},baby:{keywords:["child","boy","girl","toddler"],char:'\u{1f476}',fitzpatrick_scale:!0,category:"people"},child:{keywords:["gender-neutral","young"],char:'\u{1f9d2}',fitzpatrick_scale:!0,category:"people"},boy:{keywords:["man","male","guy","teenager"],char:'\u{1f466}',fitzpatrick_scale:!0,category:"people"},girl:{keywords:["female","woman","teenager"],char:'\u{1f467}',fitzpatrick_scale:!0,category:"people"},adult:{keywords:["gender-neutral","person"],char:'\u{1f9d1}',fitzpatrick_scale:!0,category:"people"},man:{keywords:["mustache","father","dad","guy","classy","sir","moustache"],char:'\u{1f468}',fitzpatrick_scale:!0,category:"people"},woman:{keywords:["female","girls","lady"],char:'\u{1f469}',fitzpatrick_scale:!0,category:"people"},blonde_woman:{keywords:["woman","female","girl","blonde","person"],char:'\u{1f471}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"people"},blonde_man:{keywords:["man","male","boy","blonde","guy","person"],char:'\u{1f471}',fitzpatrick_scale:!0,category:"people"},bearded_person:{keywords:["person","bewhiskered"],char:'\u{1f9d4}',fitzpatrick_scale:!0,category:"people"},older_adult:{keywords:["human","elder","senior","gender-neutral"],char:'\u{1f9d3}',fitzpatrick_scale:!0,category:"people"},older_man:{keywords:["human","male","men","old","elder","senior"],char:'\u{1f474}',fitzpatrick_scale:!0,category:"people"},older_woman:{keywords:["human","female","women","lady","old","elder","senior"],char:'\u{1f475}',fitzpatrick_scale:!0,category:"people"},man_with_gua_pi_mao:{keywords:["male","boy","chinese"],char:'\u{1f472}',fitzpatrick_scale:!0,category:"people"},woman_with_headscarf:{keywords:["female","hijab","mantilla","tichel"],char:'\u{1f9d5}',fitzpatrick_scale:!0,category:"people"},woman_with_turban:{keywords:["female","indian","hinduism","arabs","woman"],char:'\u{1f473}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"people"},man_with_turban:{keywords:["male","indian","hinduism","arabs"],char:'\u{1f473}',fitzpatrick_scale:!0,category:"people"},policewoman:{keywords:["woman","police","law","legal","enforcement","arrest","911","female"],char:'\u{1f46e}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"people"},policeman:{keywords:["man","police","law","legal","enforcement","arrest","911"],char:'\u{1f46e}',fitzpatrick_scale:!0,category:"people"},construction_worker_woman:{keywords:["female","human","wip","build","construction","worker","labor","woman"],char:'\u{1f477}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"people"},construction_worker_man:{keywords:["male","human","wip","guy","build","construction","worker","labor"],char:'\u{1f477}',fitzpatrick_scale:!0,category:"people"},guardswoman:{keywords:["uk","gb","british","female","royal","woman"],char:'\u{1f482}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"people"},guardsman:{keywords:["uk","gb","british","male","guy","royal"],char:'\u{1f482}',fitzpatrick_scale:!0,category:"people"},female_detective:{keywords:["human","spy","detective","female","woman"],char:'\u{1f575}\ufe0f\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"people"},male_detective:{keywords:["human","spy","detective"],char:'\u{1f575}',fitzpatrick_scale:!0,category:"people"},woman_health_worker:{keywords:["doctor","nurse","therapist","healthcare","woman","human"],char:'\u{1f469}\u200d\u2695\ufe0f',fitzpatrick_scale:!0,category:"people"},man_health_worker:{keywords:["doctor","nurse","therapist","healthcare","man","human"],char:'\u{1f468}\u200d\u2695\ufe0f',fitzpatrick_scale:!0,category:"people"},woman_farmer:{keywords:["rancher","gardener","woman","human"],char:'\u{1f469}\u200d\u{1f33e}',fitzpatrick_scale:!0,category:"people"},man_farmer:{keywords:["rancher","gardener","man","human"],char:'\u{1f468}\u200d\u{1f33e}',fitzpatrick_scale:!0,category:"people"},woman_cook:{keywords:["chef","woman","human"],char:'\u{1f469}\u200d\u{1f373}',fitzpatrick_scale:!0,category:"people"},man_cook:{keywords:["chef","man","human"],char:'\u{1f468}\u200d\u{1f373}',fitzpatrick_scale:!0,category:"people"},woman_student:{keywords:["graduate","woman","human"],char:'\u{1f469}\u200d\u{1f393}',fitzpatrick_scale:!0,category:"people"},man_student:{keywords:["graduate","man","human"],char:'\u{1f468}\u200d\u{1f393}',fitzpatrick_scale:!0,category:"people"},woman_singer:{keywords:["rockstar","entertainer","woman","human"],char:'\u{1f469}\u200d\u{1f3a4}',fitzpatrick_scale:!0,category:"people"},man_singer:{keywords:["rockstar","entertainer","man","human"],char:'\u{1f468}\u200d\u{1f3a4}',fitzpatrick_scale:!0,category:"people"},woman_teacher:{keywords:["instructor","professor","woman","human"],char:'\u{1f469}\u200d\u{1f3eb}',fitzpatrick_scale:!0,category:"people"},man_teacher:{keywords:["instructor","professor","man","human"],char:'\u{1f468}\u200d\u{1f3eb}',fitzpatrick_scale:!0,category:"people"},woman_factory_worker:{keywords:["assembly","industrial","woman","human"],char:'\u{1f469}\u200d\u{1f3ed}',fitzpatrick_scale:!0,category:"people"},man_factory_worker:{keywords:["assembly","industrial","man","human"],char:'\u{1f468}\u200d\u{1f3ed}',fitzpatrick_scale:!0,category:"people"},woman_technologist:{keywords:["coder","developer","engineer","programmer","software","woman","human","laptop","computer"],char:'\u{1f469}\u200d\u{1f4bb}',fitzpatrick_scale:!0,category:"people"},man_technologist:{keywords:["coder","developer","engineer","programmer","software","man","human","laptop","computer"],char:'\u{1f468}\u200d\u{1f4bb}',fitzpatrick_scale:!0,category:"people"},woman_office_worker:{keywords:["business","manager","woman","human"],char:'\u{1f469}\u200d\u{1f4bc}',fitzpatrick_scale:!0,category:"people"},man_office_worker:{keywords:["business","manager","man","human"],char:'\u{1f468}\u200d\u{1f4bc}',fitzpatrick_scale:!0,category:"people"},woman_mechanic:{keywords:["plumber","woman","human","wrench"],char:'\u{1f469}\u200d\u{1f527}',fitzpatrick_scale:!0,category:"people"},man_mechanic:{keywords:["plumber","man","human","wrench"],char:'\u{1f468}\u200d\u{1f527}',fitzpatrick_scale:!0,category:"people"},woman_scientist:{keywords:["biologist","chemist","engineer","physicist","woman","human"],char:'\u{1f469}\u200d\u{1f52c}',fitzpatrick_scale:!0,category:"people"},man_scientist:{keywords:["biologist","chemist","engineer","physicist","man","human"],char:'\u{1f468}\u200d\u{1f52c}',fitzpatrick_scale:!0,category:"people"},woman_artist:{keywords:["painter","woman","human"],char:'\u{1f469}\u200d\u{1f3a8}',fitzpatrick_scale:!0,category:"people"},man_artist:{keywords:["painter","man","human"],char:'\u{1f468}\u200d\u{1f3a8}',fitzpatrick_scale:!0,category:"people"},woman_firefighter:{keywords:["fireman","woman","human"],char:'\u{1f469}\u200d\u{1f692}',fitzpatrick_scale:!0,category:"people"},man_firefighter:{keywords:["fireman","man","human"],char:'\u{1f468}\u200d\u{1f692}',fitzpatrick_scale:!0,category:"people"},woman_pilot:{keywords:["aviator","plane","woman","human"],char:'\u{1f469}\u200d\u2708\ufe0f',fitzpatrick_scale:!0,category:"people"},man_pilot:{keywords:["aviator","plane","man","human"],char:'\u{1f468}\u200d\u2708\ufe0f',fitzpatrick_scale:!0,category:"people"},woman_astronaut:{keywords:["space","rocket","woman","human"],char:'\u{1f469}\u200d\u{1f680}',fitzpatrick_scale:!0,category:"people"},man_astronaut:{keywords:["space","rocket","man","human"],char:'\u{1f468}\u200d\u{1f680}',fitzpatrick_scale:!0,category:"people"},woman_judge:{keywords:["justice","court","woman","human"],char:'\u{1f469}\u200d\u2696\ufe0f',fitzpatrick_scale:!0,category:"people"},man_judge:{keywords:["justice","court","man","human"],char:'\u{1f468}\u200d\u2696\ufe0f',fitzpatrick_scale:!0,category:"people"},woman_superhero:{keywords:["woman","female","good","heroine","superpowers"],char:'\u{1f9b8}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"people"},man_superhero:{keywords:["man","male","good","hero","superpowers"],char:'\u{1f9b8}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"people"},woman_supervillain:{keywords:["woman","female","evil","bad","criminal","heroine","superpowers"],char:'\u{1f9b9}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"people"},man_supervillain:{keywords:["man","male","evil","bad","criminal","hero","superpowers"],char:'\u{1f9b9}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"people"},mrs_claus:{keywords:["woman","female","xmas","mother christmas"],char:'\u{1f936}',fitzpatrick_scale:!0,category:"people"},santa:{keywords:["festival","man","male","xmas","father christmas"],char:'\u{1f385}',fitzpatrick_scale:!0,category:"people"},sorceress:{keywords:["woman","female","mage","witch"],char:'\u{1f9d9}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"people"},wizard:{keywords:["man","male","mage","sorcerer"],char:'\u{1f9d9}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"people"},woman_elf:{keywords:["woman","female"],char:'\u{1f9dd}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"people"},man_elf:{keywords:["man","male"],char:'\u{1f9dd}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"people"},woman_vampire:{keywords:["woman","female"],char:'\u{1f9db}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"people"},man_vampire:{keywords:["man","male","dracula"],char:'\u{1f9db}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"people"},woman_zombie:{keywords:["woman","female","undead","walking dead"],char:'\u{1f9df}\u200d\u2640\ufe0f',fitzpatrick_scale:!1,category:"people"},man_zombie:{keywords:["man","male","dracula","undead","walking dead"],char:'\u{1f9df}\u200d\u2642\ufe0f',fitzpatrick_scale:!1,category:"people"},woman_genie:{keywords:["woman","female"],char:'\u{1f9de}\u200d\u2640\ufe0f',fitzpatrick_scale:!1,category:"people"},man_genie:{keywords:["man","male"],char:'\u{1f9de}\u200d\u2642\ufe0f',fitzpatrick_scale:!1,category:"people"},mermaid:{keywords:["woman","female","merwoman","ariel"],char:'\u{1f9dc}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"people"},merman:{keywords:["man","male","triton"],char:'\u{1f9dc}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"people"},woman_fairy:{keywords:["woman","female"],char:'\u{1f9da}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"people"},man_fairy:{keywords:["man","male"],char:'\u{1f9da}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"people"},angel:{keywords:["heaven","wings","halo"],char:'\u{1f47c}',fitzpatrick_scale:!0,category:"people"},pregnant_woman:{keywords:["baby"],char:'\u{1f930}',fitzpatrick_scale:!0,category:"people"},breastfeeding:{keywords:["nursing","baby"],char:'\u{1f931}',fitzpatrick_scale:!0,category:"people"},princess:{keywords:["girl","woman","female","blond","crown","royal","queen"],char:'\u{1f478}',fitzpatrick_scale:!0,category:"people"},prince:{keywords:["boy","man","male","crown","royal","king"],char:'\u{1f934}',fitzpatrick_scale:!0,category:"people"},bride_with_veil:{keywords:["couple","marriage","wedding","woman","bride"],char:'\u{1f470}',fitzpatrick_scale:!0,category:"people"},man_in_tuxedo:{keywords:["couple","marriage","wedding","groom"],char:'\u{1f935}',fitzpatrick_scale:!0,category:"people"},running_woman:{keywords:["woman","walking","exercise","race","running","female"],char:'\u{1f3c3}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"people"},running_man:{keywords:["man","walking","exercise","race","running"],char:'\u{1f3c3}',fitzpatrick_scale:!0,category:"people"},walking_woman:{keywords:["human","feet","steps","woman","female"],char:'\u{1f6b6}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"people"},walking_man:{keywords:["human","feet","steps"],char:'\u{1f6b6}',fitzpatrick_scale:!0,category:"people"},dancer:{keywords:["female","girl","woman","fun"],char:'\u{1f483}',fitzpatrick_scale:!0,category:"people"},man_dancing:{keywords:["male","boy","fun","dancer"],char:'\u{1f57a}',fitzpatrick_scale:!0,category:"people"},dancing_women:{keywords:["female","bunny","women","girls"],char:'\u{1f46f}',fitzpatrick_scale:!1,category:"people"},dancing_men:{keywords:["male","bunny","men","boys"],char:'\u{1f46f}\u200d\u2642\ufe0f',fitzpatrick_scale:!1,category:"people"},couple:{keywords:["pair","people","human","love","date","dating","like","affection","valentines","marriage"],char:'\u{1f46b}',fitzpatrick_scale:!1,category:"people"},two_men_holding_hands:{keywords:["pair","couple","love","like","bromance","friendship","people","human"],char:'\u{1f46c}',fitzpatrick_scale:!1,category:"people"},two_women_holding_hands:{keywords:["pair","friendship","couple","love","like","female","people","human"],char:'\u{1f46d}',fitzpatrick_scale:!1,category:"people"},bowing_woman:{keywords:["woman","female","girl"],char:'\u{1f647}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"people"},bowing_man:{keywords:["man","male","boy"],char:'\u{1f647}',fitzpatrick_scale:!0,category:"people"},man_facepalming:{keywords:["man","male","boy","disbelief"],char:'\u{1f926}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"people"},woman_facepalming:{keywords:["woman","female","girl","disbelief"],char:'\u{1f926}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"people"},woman_shrugging:{keywords:["woman","female","girl","confused","indifferent","doubt"],char:'\u{1f937}',fitzpatrick_scale:!0,category:"people"},man_shrugging:{keywords:["man","male","boy","confused","indifferent","doubt"],char:'\u{1f937}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"people"},tipping_hand_woman:{keywords:["female","girl","woman","human","information"],char:'\u{1f481}',fitzpatrick_scale:!0,category:"people"},tipping_hand_man:{keywords:["male","boy","man","human","information"],char:'\u{1f481}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"people"},no_good_woman:{keywords:["female","girl","woman","nope"],char:'\u{1f645}',fitzpatrick_scale:!0,category:"people"},no_good_man:{keywords:["male","boy","man","nope"],char:'\u{1f645}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"people"},ok_woman:{keywords:["women","girl","female","pink","human","woman"],char:'\u{1f646}',fitzpatrick_scale:!0,category:"people"},ok_man:{keywords:["men","boy","male","blue","human","man"],char:'\u{1f646}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"people"},raising_hand_woman:{keywords:["female","girl","woman"],char:'\u{1f64b}',fitzpatrick_scale:!0,category:"people"},raising_hand_man:{keywords:["male","boy","man"],char:'\u{1f64b}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"people"},pouting_woman:{keywords:["female","girl","woman"],char:'\u{1f64e}',fitzpatrick_scale:!0,category:"people"},pouting_man:{keywords:["male","boy","man"],char:'\u{1f64e}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"people"},frowning_woman:{keywords:["female","girl","woman","sad","depressed","discouraged","unhappy"],char:'\u{1f64d}',fitzpatrick_scale:!0,category:"people"},frowning_man:{keywords:["male","boy","man","sad","depressed","discouraged","unhappy"],char:'\u{1f64d}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"people"},haircut_woman:{keywords:["female","girl","woman"],char:'\u{1f487}',fitzpatrick_scale:!0,category:"people"},haircut_man:{keywords:["male","boy","man"],char:'\u{1f487}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"people"},massage_woman:{keywords:["female","girl","woman","head"],char:'\u{1f486}',fitzpatrick_scale:!0,category:"people"},massage_man:{keywords:["male","boy","man","head"],char:'\u{1f486}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"people"},woman_in_steamy_room:{keywords:["female","woman","spa","steamroom","sauna"],char:'\u{1f9d6}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"people"},man_in_steamy_room:{keywords:["male","man","spa","steamroom","sauna"],char:'\u{1f9d6}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"people"},couple_with_heart_woman_man:{keywords:["pair","love","like","affection","human","dating","valentines","marriage"],char:'\u{1f491}',fitzpatrick_scale:!1,category:"people"},couple_with_heart_woman_woman:{keywords:["pair","love","like","affection","human","dating","valentines","marriage"],char:'\u{1f469}\u200d\u2764\ufe0f\u200d\u{1f469}',fitzpatrick_scale:!1,category:"people"},couple_with_heart_man_man:{keywords:["pair","love","like","affection","human","dating","valentines","marriage"],char:'\u{1f468}\u200d\u2764\ufe0f\u200d\u{1f468}',fitzpatrick_scale:!1,category:"people"},couplekiss_man_woman:{keywords:["pair","valentines","love","like","dating","marriage"],char:'\u{1f48f}',fitzpatrick_scale:!1,category:"people"},couplekiss_woman_woman:{keywords:["pair","valentines","love","like","dating","marriage"],char:'\u{1f469}\u200d\u2764\ufe0f\u200d\u{1f48b}\u200d\u{1f469}',fitzpatrick_scale:!1,category:"people"},couplekiss_man_man:{keywords:["pair","valentines","love","like","dating","marriage"],char:'\u{1f468}\u200d\u2764\ufe0f\u200d\u{1f48b}\u200d\u{1f468}',fitzpatrick_scale:!1,category:"people"},family_man_woman_boy:{keywords:["home","parents","child","mom","dad","father","mother","people","human"],char:'\u{1f46a}',fitzpatrick_scale:!1,category:"people"},family_man_woman_girl:{keywords:["home","parents","people","human","child"],char:'\u{1f468}\u200d\u{1f469}\u200d\u{1f467}',fitzpatrick_scale:!1,category:"people"},family_man_woman_girl_boy:{keywords:["home","parents","people","human","children"],char:'\u{1f468}\u200d\u{1f469}\u200d\u{1f467}\u200d\u{1f466}',fitzpatrick_scale:!1,category:"people"},family_man_woman_boy_boy:{keywords:["home","parents","people","human","children"],char:'\u{1f468}\u200d\u{1f469}\u200d\u{1f466}\u200d\u{1f466}',fitzpatrick_scale:!1,category:"people"},family_man_woman_girl_girl:{keywords:["home","parents","people","human","children"],char:'\u{1f468}\u200d\u{1f469}\u200d\u{1f467}\u200d\u{1f467}',fitzpatrick_scale:!1,category:"people"},family_woman_woman_boy:{keywords:["home","parents","people","human","children"],char:'\u{1f469}\u200d\u{1f469}\u200d\u{1f466}',fitzpatrick_scale:!1,category:"people"},family_woman_woman_girl:{keywords:["home","parents","people","human","children"],char:'\u{1f469}\u200d\u{1f469}\u200d\u{1f467}',fitzpatrick_scale:!1,category:"people"},family_woman_woman_girl_boy:{keywords:["home","parents","people","human","children"],char:'\u{1f469}\u200d\u{1f469}\u200d\u{1f467}\u200d\u{1f466}',fitzpatrick_scale:!1,category:"people"},family_woman_woman_boy_boy:{keywords:["home","parents","people","human","children"],char:'\u{1f469}\u200d\u{1f469}\u200d\u{1f466}\u200d\u{1f466}',fitzpatrick_scale:!1,category:"people"},family_woman_woman_girl_girl:{keywords:["home","parents","people","human","children"],char:'\u{1f469}\u200d\u{1f469}\u200d\u{1f467}\u200d\u{1f467}',fitzpatrick_scale:!1,category:"people"},family_man_man_boy:{keywords:["home","parents","people","human","children"],char:'\u{1f468}\u200d\u{1f468}\u200d\u{1f466}',fitzpatrick_scale:!1,category:"people"},family_man_man_girl:{keywords:["home","parents","people","human","children"],char:'\u{1f468}\u200d\u{1f468}\u200d\u{1f467}',fitzpatrick_scale:!1,category:"people"},family_man_man_girl_boy:{keywords:["home","parents","people","human","children"],char:'\u{1f468}\u200d\u{1f468}\u200d\u{1f467}\u200d\u{1f466}',fitzpatrick_scale:!1,category:"people"},family_man_man_boy_boy:{keywords:["home","parents","people","human","children"],char:'\u{1f468}\u200d\u{1f468}\u200d\u{1f466}\u200d\u{1f466}',fitzpatrick_scale:!1,category:"people"},family_man_man_girl_girl:{keywords:["home","parents","people","human","children"],char:'\u{1f468}\u200d\u{1f468}\u200d\u{1f467}\u200d\u{1f467}',fitzpatrick_scale:!1,category:"people"},family_woman_boy:{keywords:["home","parent","people","human","child"],char:'\u{1f469}\u200d\u{1f466}',fitzpatrick_scale:!1,category:"people"},family_woman_girl:{keywords:["home","parent","people","human","child"],char:'\u{1f469}\u200d\u{1f467}',fitzpatrick_scale:!1,category:"people"},family_woman_girl_boy:{keywords:["home","parent","people","human","children"],char:'\u{1f469}\u200d\u{1f467}\u200d\u{1f466}',fitzpatrick_scale:!1,category:"people"},family_woman_boy_boy:{keywords:["home","parent","people","human","children"],char:'\u{1f469}\u200d\u{1f466}\u200d\u{1f466}',fitzpatrick_scale:!1,category:"people"},family_woman_girl_girl:{keywords:["home","parent","people","human","children"],char:'\u{1f469}\u200d\u{1f467}\u200d\u{1f467}',fitzpatrick_scale:!1,category:"people"},family_man_boy:{keywords:["home","parent","people","human","child"],char:'\u{1f468}\u200d\u{1f466}',fitzpatrick_scale:!1,category:"people"},family_man_girl:{keywords:["home","parent","people","human","child"],char:'\u{1f468}\u200d\u{1f467}',fitzpatrick_scale:!1,category:"people"},family_man_girl_boy:{keywords:["home","parent","people","human","children"],char:'\u{1f468}\u200d\u{1f467}\u200d\u{1f466}',fitzpatrick_scale:!1,category:"people"},family_man_boy_boy:{keywords:["home","parent","people","human","children"],char:'\u{1f468}\u200d\u{1f466}\u200d\u{1f466}',fitzpatrick_scale:!1,category:"people"},family_man_girl_girl:{keywords:["home","parent","people","human","children"],char:'\u{1f468}\u200d\u{1f467}\u200d\u{1f467}',fitzpatrick_scale:!1,category:"people"},yarn:{keywords:["ball","crochet","knit"],char:'\u{1f9f6}',fitzpatrick_scale:!1,category:"people"},thread:{keywords:["needle","sewing","spool","string"],char:'\u{1f9f5}',fitzpatrick_scale:!1,category:"people"},coat:{keywords:["jacket"],char:'\u{1f9e5}',fitzpatrick_scale:!1,category:"people"},labcoat:{keywords:["doctor","experiment","scientist","chemist"],char:'\u{1f97c}',fitzpatrick_scale:!1,category:"people"},womans_clothes:{keywords:["fashion","shopping_bags","female"],char:'\u{1f45a}',fitzpatrick_scale:!1,category:"people"},tshirt:{keywords:["fashion","cloth","casual","shirt","tee"],char:'\u{1f455}',fitzpatrick_scale:!1,category:"people"},jeans:{keywords:["fashion","shopping"],char:'\u{1f456}',fitzpatrick_scale:!1,category:"people"},necktie:{keywords:["shirt","suitup","formal","fashion","cloth","business"],char:'\u{1f454}',fitzpatrick_scale:!1,category:"people"},dress:{keywords:["clothes","fashion","shopping"],char:'\u{1f457}',fitzpatrick_scale:!1,category:"people"},bikini:{keywords:["swimming","female","woman","girl","fashion","beach","summer"],char:'\u{1f459}',fitzpatrick_scale:!1,category:"people"},kimono:{keywords:["dress","fashion","women","female","japanese"],char:'\u{1f458}',fitzpatrick_scale:!1,category:"people"},lipstick:{keywords:["female","girl","fashion","woman"],char:'\u{1f484}',fitzpatrick_scale:!1,category:"people"},kiss:{keywords:["face","lips","love","like","affection","valentines"],char:'\u{1f48b}',fitzpatrick_scale:!1,category:"people"},footprints:{keywords:["feet","tracking","walking","beach"],char:'\u{1f463}',fitzpatrick_scale:!1,category:"people"},flat_shoe:{keywords:["ballet","slip-on","slipper"],char:'\u{1f97f}',fitzpatrick_scale:!1,category:"people"},high_heel:{keywords:["fashion","shoes","female","pumps","stiletto"],char:'\u{1f460}',fitzpatrick_scale:!1,category:"people"},sandal:{keywords:["shoes","fashion","flip flops"],char:'\u{1f461}',fitzpatrick_scale:!1,category:"people"},boot:{keywords:["shoes","fashion"],char:'\u{1f462}',fitzpatrick_scale:!1,category:"people"},mans_shoe:{keywords:["fashion","male"],char:'\u{1f45e}',fitzpatrick_scale:!1,category:"people"},athletic_shoe:{keywords:["shoes","sports","sneakers"],char:'\u{1f45f}',fitzpatrick_scale:!1,category:"people"},hiking_boot:{keywords:["backpacking","camping","hiking"],char:'\u{1f97e}',fitzpatrick_scale:!1,category:"people"},socks:{keywords:["stockings","clothes"],char:'\u{1f9e6}',fitzpatrick_scale:!1,category:"people"},gloves:{keywords:["hands","winter","clothes"],char:'\u{1f9e4}',fitzpatrick_scale:!1,category:"people"},scarf:{keywords:["neck","winter","clothes"],char:'\u{1f9e3}',fitzpatrick_scale:!1,category:"people"},womans_hat:{keywords:["fashion","accessories","female","lady","spring"],char:'\u{1f452}',fitzpatrick_scale:!1,category:"people"},tophat:{keywords:["magic","gentleman","classy","circus"],char:'\u{1f3a9}',fitzpatrick_scale:!1,category:"people"},billed_hat:{keywords:["cap","baseball"],char:'\u{1f9e2}',fitzpatrick_scale:!1,category:"people"},rescue_worker_helmet:{keywords:["construction","build"],char:'\u26d1',fitzpatrick_scale:!1,category:"people"},mortar_board:{keywords:["school","college","degree","university","graduation","cap","hat","legal","learn","education"],char:'\u{1f393}',fitzpatrick_scale:!1,category:"people"},crown:{keywords:["king","kod","leader","royalty","lord"],char:'\u{1f451}',fitzpatrick_scale:!1,category:"people"},school_satchel:{keywords:["student","education","bag","backpack"],char:'\u{1f392}',fitzpatrick_scale:!1,category:"people"},luggage:{keywords:["packing","travel"],char:'\u{1f9f3}',fitzpatrick_scale:!1,category:"people"},pouch:{keywords:["bag","accessories","shopping"],char:'\u{1f45d}',fitzpatrick_scale:!1,category:"people"},purse:{keywords:["fashion","accessories","money","sales","shopping"],char:'\u{1f45b}',fitzpatrick_scale:!1,category:"people"},handbag:{keywords:["fashion","accessory","accessories","shopping"],char:'\u{1f45c}',fitzpatrick_scale:!1,category:"people"},briefcase:{keywords:["business","documents","work","law","legal","job","career"],char:'\u{1f4bc}',fitzpatrick_scale:!1,category:"people"},eyeglasses:{keywords:["fashion","accessories","eyesight","nerdy","dork","geek"],char:'\u{1f453}',fitzpatrick_scale:!1,category:"people"},dark_sunglasses:{keywords:["face","cool","accessories"],char:'\u{1f576}',fitzpatrick_scale:!1,category:"people"},goggles:{keywords:["eyes","protection","safety"],char:'\u{1f97d}',fitzpatrick_scale:!1,category:"people"},ring:{keywords:["wedding","propose","marriage","valentines","diamond","fashion","jewelry","gem","engagement"],char:'\u{1f48d}',fitzpatrick_scale:!1,category:"people"},closed_umbrella:{keywords:["weather","rain","drizzle"],char:'\u{1f302}',fitzpatrick_scale:!1,category:"people"},dog:{keywords:["animal","friend","nature","woof","puppy","pet","faithful"],char:'\u{1f436}',fitzpatrick_scale:!1,category:"animals_and_nature"},cat:{keywords:["animal","meow","nature","pet","kitten"],char:'\u{1f431}',fitzpatrick_scale:!1,category:"animals_and_nature"},mouse:{keywords:["animal","nature","cheese_wedge","rodent"],char:'\u{1f42d}',fitzpatrick_scale:!1,category:"animals_and_nature"},hamster:{keywords:["animal","nature"],char:'\u{1f439}',fitzpatrick_scale:!1,category:"animals_and_nature"},rabbit:{keywords:["animal","nature","pet","spring","magic","bunny"],char:'\u{1f430}',fitzpatrick_scale:!1,category:"animals_and_nature"},fox_face:{keywords:["animal","nature","face"],char:'\u{1f98a}',fitzpatrick_scale:!1,category:"animals_and_nature"},bear:{keywords:["animal","nature","wild"],char:'\u{1f43b}',fitzpatrick_scale:!1,category:"animals_and_nature"},panda_face:{keywords:["animal","nature","panda"],char:'\u{1f43c}',fitzpatrick_scale:!1,category:"animals_and_nature"},koala:{keywords:["animal","nature"],char:'\u{1f428}',fitzpatrick_scale:!1,category:"animals_and_nature"},tiger:{keywords:["animal","cat","danger","wild","nature","roar"],char:'\u{1f42f}',fitzpatrick_scale:!1,category:"animals_and_nature"},lion:{keywords:["animal","nature"],char:'\u{1f981}',fitzpatrick_scale:!1,category:"animals_and_nature"},cow:{keywords:["beef","ox","animal","nature","moo","milk"],char:'\u{1f42e}',fitzpatrick_scale:!1,category:"animals_and_nature"},pig:{keywords:["animal","oink","nature"],char:'\u{1f437}',fitzpatrick_scale:!1,category:"animals_and_nature"},pig_nose:{keywords:["animal","oink"],char:'\u{1f43d}',fitzpatrick_scale:!1,category:"animals_and_nature"},frog:{keywords:["animal","nature","croak","toad"],char:'\u{1f438}',fitzpatrick_scale:!1,category:"animals_and_nature"},squid:{keywords:["animal","nature","ocean","sea"],char:'\u{1f991}',fitzpatrick_scale:!1,category:"animals_and_nature"},octopus:{keywords:["animal","creature","ocean","sea","nature","beach"],char:'\u{1f419}',fitzpatrick_scale:!1,category:"animals_and_nature"},shrimp:{keywords:["animal","ocean","nature","seafood"],char:'\u{1f990}',fitzpatrick_scale:!1,category:"animals_and_nature"},monkey_face:{keywords:["animal","nature","circus"],char:'\u{1f435}',fitzpatrick_scale:!1,category:"animals_and_nature"},gorilla:{keywords:["animal","nature","circus"],char:'\u{1f98d}',fitzpatrick_scale:!1,category:"animals_and_nature"},see_no_evil:{keywords:["monkey","animal","nature","haha"],char:'\u{1f648}',fitzpatrick_scale:!1,category:"animals_and_nature"},hear_no_evil:{keywords:["animal","monkey","nature"],char:'\u{1f649}',fitzpatrick_scale:!1,category:"animals_and_nature"},speak_no_evil:{keywords:["monkey","animal","nature","omg"],char:'\u{1f64a}',fitzpatrick_scale:!1,category:"animals_and_nature"},monkey:{keywords:["animal","nature","banana","circus"],char:'\u{1f412}',fitzpatrick_scale:!1,category:"animals_and_nature"},chicken:{keywords:["animal","cluck","nature","bird"],char:'\u{1f414}',fitzpatrick_scale:!1,category:"animals_and_nature"},penguin:{keywords:["animal","nature"],char:'\u{1f427}',fitzpatrick_scale:!1,category:"animals_and_nature"},bird:{keywords:["animal","nature","fly","tweet","spring"],char:'\u{1f426}',fitzpatrick_scale:!1,category:"animals_and_nature"},baby_chick:{keywords:["animal","chicken","bird"],char:'\u{1f424}',fitzpatrick_scale:!1,category:"animals_and_nature"},hatching_chick:{keywords:["animal","chicken","egg","born","baby","bird"],char:'\u{1f423}',fitzpatrick_scale:!1,category:"animals_and_nature"},hatched_chick:{keywords:["animal","chicken","baby","bird"],char:'\u{1f425}',fitzpatrick_scale:!1,category:"animals_and_nature"},duck:{keywords:["animal","nature","bird","mallard"],char:'\u{1f986}',fitzpatrick_scale:!1,category:"animals_and_nature"},eagle:{keywords:["animal","nature","bird"],char:'\u{1f985}',fitzpatrick_scale:!1,category:"animals_and_nature"},owl:{keywords:["animal","nature","bird","hoot"],char:'\u{1f989}',fitzpatrick_scale:!1,category:"animals_and_nature"},bat:{keywords:["animal","nature","blind","vampire"],char:'\u{1f987}',fitzpatrick_scale:!1,category:"animals_and_nature"},wolf:{keywords:["animal","nature","wild"],char:'\u{1f43a}',fitzpatrick_scale:!1,category:"animals_and_nature"},boar:{keywords:["animal","nature"],char:'\u{1f417}',fitzpatrick_scale:!1,category:"animals_and_nature"},horse:{keywords:["animal","brown","nature"],char:'\u{1f434}',fitzpatrick_scale:!1,category:"animals_and_nature"},unicorn:{keywords:["animal","nature","mystical"],char:'\u{1f984}',fitzpatrick_scale:!1,category:"animals_and_nature"},honeybee:{keywords:["animal","insect","nature","bug","spring","honey"],char:'\u{1f41d}',fitzpatrick_scale:!1,category:"animals_and_nature"},bug:{keywords:["animal","insect","nature","worm"],char:'\u{1f41b}',fitzpatrick_scale:!1,category:"animals_and_nature"},butterfly:{keywords:["animal","insect","nature","caterpillar"],char:'\u{1f98b}',fitzpatrick_scale:!1,category:"animals_and_nature"},snail:{keywords:["slow","animal","shell"],char:'\u{1f40c}',fitzpatrick_scale:!1,category:"animals_and_nature"},beetle:{keywords:["animal","insect","nature","ladybug"],char:'\u{1f41e}',fitzpatrick_scale:!1,category:"animals_and_nature"},ant:{keywords:["animal","insect","nature","bug"],char:'\u{1f41c}',fitzpatrick_scale:!1,category:"animals_and_nature"},grasshopper:{keywords:["animal","cricket","chirp"],char:'\u{1f997}',fitzpatrick_scale:!1,category:"animals_and_nature"},spider:{keywords:["animal","arachnid"],char:'\u{1f577}',fitzpatrick_scale:!1,category:"animals_and_nature"},scorpion:{keywords:["animal","arachnid"],char:'\u{1f982}',fitzpatrick_scale:!1,category:"animals_and_nature"},crab:{keywords:["animal","crustacean"],char:'\u{1f980}',fitzpatrick_scale:!1,category:"animals_and_nature"},snake:{keywords:["animal","evil","nature","hiss","python"],char:'\u{1f40d}',fitzpatrick_scale:!1,category:"animals_and_nature"},lizard:{keywords:["animal","nature","reptile"],char:'\u{1f98e}',fitzpatrick_scale:!1,category:"animals_and_nature"},"t-rex":{keywords:["animal","nature","dinosaur","tyrannosaurus","extinct"],char:'\u{1f996}',fitzpatrick_scale:!1,category:"animals_and_nature"},sauropod:{keywords:["animal","nature","dinosaur","brachiosaurus","brontosaurus","diplodocus","extinct"],char:'\u{1f995}',fitzpatrick_scale:!1,category:"animals_and_nature"},turtle:{keywords:["animal","slow","nature","tortoise"],char:'\u{1f422}',fitzpatrick_scale:!1,category:"animals_and_nature"},tropical_fish:{keywords:["animal","swim","ocean","beach","nemo"],char:'\u{1f420}',fitzpatrick_scale:!1,category:"animals_and_nature"},fish:{keywords:["animal","food","nature"],char:'\u{1f41f}',fitzpatrick_scale:!1,category:"animals_and_nature"},blowfish:{keywords:["animal","nature","food","sea","ocean"],char:'\u{1f421}',fitzpatrick_scale:!1,category:"animals_and_nature"},dolphin:{keywords:["animal","nature","fish","sea","ocean","flipper","fins","beach"],char:'\u{1f42c}',fitzpatrick_scale:!1,category:"animals_and_nature"},shark:{keywords:["animal","nature","fish","sea","ocean","jaws","fins","beach"],char:'\u{1f988}',fitzpatrick_scale:!1,category:"animals_and_nature"},whale:{keywords:["animal","nature","sea","ocean"],char:'\u{1f433}',fitzpatrick_scale:!1,category:"animals_and_nature"},whale2:{keywords:["animal","nature","sea","ocean"],char:'\u{1f40b}',fitzpatrick_scale:!1,category:"animals_and_nature"},crocodile:{keywords:["animal","nature","reptile","lizard","alligator"],char:'\u{1f40a}',fitzpatrick_scale:!1,category:"animals_and_nature"},leopard:{keywords:["animal","nature"],char:'\u{1f406}',fitzpatrick_scale:!1,category:"animals_and_nature"},zebra:{keywords:["animal","nature","stripes","safari"],char:'\u{1f993}',fitzpatrick_scale:!1,category:"animals_and_nature"},tiger2:{keywords:["animal","nature","roar"],char:'\u{1f405}',fitzpatrick_scale:!1,category:"animals_and_nature"},water_buffalo:{keywords:["animal","nature","ox","cow"],char:'\u{1f403}',fitzpatrick_scale:!1,category:"animals_and_nature"},ox:{keywords:["animal","cow","beef"],char:'\u{1f402}',fitzpatrick_scale:!1,category:"animals_and_nature"},cow2:{keywords:["beef","ox","animal","nature","moo","milk"],char:'\u{1f404}',fitzpatrick_scale:!1,category:"animals_and_nature"},deer:{keywords:["animal","nature","horns","venison"],char:'\u{1f98c}',fitzpatrick_scale:!1,category:"animals_and_nature"},dromedary_camel:{keywords:["animal","hot","desert","hump"],char:'\u{1f42a}',fitzpatrick_scale:!1,category:"animals_and_nature"},camel:{keywords:["animal","nature","hot","desert","hump"],char:'\u{1f42b}',fitzpatrick_scale:!1,category:"animals_and_nature"},giraffe:{keywords:["animal","nature","spots","safari"],char:'\u{1f992}',fitzpatrick_scale:!1,category:"animals_and_nature"},elephant:{keywords:["animal","nature","nose","th","circus"],char:'\u{1f418}',fitzpatrick_scale:!1,category:"animals_and_nature"},rhinoceros:{keywords:["animal","nature","horn"],char:'\u{1f98f}',fitzpatrick_scale:!1,category:"animals_and_nature"},goat:{keywords:["animal","nature"],char:'\u{1f410}',fitzpatrick_scale:!1,category:"animals_and_nature"},ram:{keywords:["animal","sheep","nature"],char:'\u{1f40f}',fitzpatrick_scale:!1,category:"animals_and_nature"},sheep:{keywords:["animal","nature","wool","shipit"],char:'\u{1f411}',fitzpatrick_scale:!1,category:"animals_and_nature"},racehorse:{keywords:["animal","gamble","luck"],char:'\u{1f40e}',fitzpatrick_scale:!1,category:"animals_and_nature"},pig2:{keywords:["animal","nature"],char:'\u{1f416}',fitzpatrick_scale:!1,category:"animals_and_nature"},rat:{keywords:["animal","mouse","rodent"],char:'\u{1f400}',fitzpatrick_scale:!1,category:"animals_and_nature"},mouse2:{keywords:["animal","nature","rodent"],char:'\u{1f401}',fitzpatrick_scale:!1,category:"animals_and_nature"},rooster:{keywords:["animal","nature","chicken"],char:'\u{1f413}',fitzpatrick_scale:!1,category:"animals_and_nature"},turkey:{keywords:["animal","bird"],char:'\u{1f983}',fitzpatrick_scale:!1,category:"animals_and_nature"},dove:{keywords:["animal","bird"],char:'\u{1f54a}',fitzpatrick_scale:!1,category:"animals_and_nature"},dog2:{keywords:["animal","nature","friend","doge","pet","faithful"],char:'\u{1f415}',fitzpatrick_scale:!1,category:"animals_and_nature"},poodle:{keywords:["dog","animal","101","nature","pet"],char:'\u{1f429}',fitzpatrick_scale:!1,category:"animals_and_nature"},cat2:{keywords:["animal","meow","pet","cats"],char:'\u{1f408}',fitzpatrick_scale:!1,category:"animals_and_nature"},rabbit2:{keywords:["animal","nature","pet","magic","spring"],char:'\u{1f407}',fitzpatrick_scale:!1,category:"animals_and_nature"},chipmunk:{keywords:["animal","nature","rodent","squirrel"],char:'\u{1f43f}',fitzpatrick_scale:!1,category:"animals_and_nature"},hedgehog:{keywords:["animal","nature","spiny"],char:'\u{1f994}',fitzpatrick_scale:!1,category:"animals_and_nature"},raccoon:{keywords:["animal","nature"],char:'\u{1f99d}',fitzpatrick_scale:!1,category:"animals_and_nature"},llama:{keywords:["animal","nature","alpaca"],char:'\u{1f999}',fitzpatrick_scale:!1,category:"animals_and_nature"},hippopotamus:{keywords:["animal","nature"],char:'\u{1f99b}',fitzpatrick_scale:!1,category:"animals_and_nature"},kangaroo:{keywords:["animal","nature","australia","joey","hop","marsupial"],char:'\u{1f998}',fitzpatrick_scale:!1,category:"animals_and_nature"},badger:{keywords:["animal","nature","honey"],char:'\u{1f9a1}',fitzpatrick_scale:!1,category:"animals_and_nature"},swan:{keywords:["animal","nature","bird"],char:'\u{1f9a2}',fitzpatrick_scale:!1,category:"animals_and_nature"},peacock:{keywords:["animal","nature","peahen","bird"],char:'\u{1f99a}',fitzpatrick_scale:!1,category:"animals_and_nature"},parrot:{keywords:["animal","nature","bird","pirate","talk"],char:'\u{1f99c}',fitzpatrick_scale:!1,category:"animals_and_nature"},lobster:{keywords:["animal","nature","bisque","claws","seafood"],char:'\u{1f99e}',fitzpatrick_scale:!1,category:"animals_and_nature"},mosquito:{keywords:["animal","nature","insect","malaria"],char:'\u{1f99f}',fitzpatrick_scale:!1,category:"animals_and_nature"},paw_prints:{keywords:["animal","tracking","footprints","dog","cat","pet","feet"],char:'\u{1f43e}',fitzpatrick_scale:!1,category:"animals_and_nature"},dragon:{keywords:["animal","myth","nature","chinese","green"],char:'\u{1f409}',fitzpatrick_scale:!1,category:"animals_and_nature"},dragon_face:{keywords:["animal","myth","nature","chinese","green"],char:'\u{1f432}',fitzpatrick_scale:!1,category:"animals_and_nature"},cactus:{keywords:["vegetable","plant","nature"],char:'\u{1f335}',fitzpatrick_scale:!1,category:"animals_and_nature"},christmas_tree:{keywords:["festival","vacation","december","xmas","celebration"],char:'\u{1f384}',fitzpatrick_scale:!1,category:"animals_and_nature"},evergreen_tree:{keywords:["plant","nature"],char:'\u{1f332}',fitzpatrick_scale:!1,category:"animals_and_nature"},deciduous_tree:{keywords:["plant","nature"],char:'\u{1f333}',fitzpatrick_scale:!1,category:"animals_and_nature"},palm_tree:{keywords:["plant","vegetable","nature","summer","beach","mojito","tropical"],char:'\u{1f334}',fitzpatrick_scale:!1,category:"animals_and_nature"},seedling:{keywords:["plant","nature","grass","lawn","spring"],char:'\u{1f331}',fitzpatrick_scale:!1,category:"animals_and_nature"},herb:{keywords:["vegetable","plant","medicine","weed","grass","lawn"],char:'\u{1f33f}',fitzpatrick_scale:!1,category:"animals_and_nature"},shamrock:{keywords:["vegetable","plant","nature","irish","clover"],char:'\u2618',fitzpatrick_scale:!1,category:"animals_and_nature"},four_leaf_clover:{keywords:["vegetable","plant","nature","lucky","irish"],char:'\u{1f340}',fitzpatrick_scale:!1,category:"animals_and_nature"},bamboo:{keywords:["plant","nature","vegetable","panda","pine_decoration"],char:'\u{1f38d}',fitzpatrick_scale:!1,category:"animals_and_nature"},tanabata_tree:{keywords:["plant","nature","branch","summer"],char:'\u{1f38b}',fitzpatrick_scale:!1,category:"animals_and_nature"},leaves:{keywords:["nature","plant","tree","vegetable","grass","lawn","spring"],char:'\u{1f343}',fitzpatrick_scale:!1,category:"animals_and_nature"},fallen_leaf:{keywords:["nature","plant","vegetable","leaves"],char:'\u{1f342}',fitzpatrick_scale:!1,category:"animals_and_nature"},maple_leaf:{keywords:["nature","plant","vegetable","ca","fall"],char:'\u{1f341}',fitzpatrick_scale:!1,category:"animals_and_nature"},ear_of_rice:{keywords:["nature","plant"],char:'\u{1f33e}',fitzpatrick_scale:!1,category:"animals_and_nature"},hibiscus:{keywords:["plant","vegetable","flowers","beach"],char:'\u{1f33a}',fitzpatrick_scale:!1,category:"animals_and_nature"},sunflower:{keywords:["nature","plant","fall"],char:'\u{1f33b}',fitzpatrick_scale:!1,category:"animals_and_nature"},rose:{keywords:["flowers","valentines","love","spring"],char:'\u{1f339}',fitzpatrick_scale:!1,category:"animals_and_nature"},wilted_flower:{keywords:["plant","nature","flower"],char:'\u{1f940}',fitzpatrick_scale:!1,category:"animals_and_nature"},tulip:{keywords:["flowers","plant","nature","summer","spring"],char:'\u{1f337}',fitzpatrick_scale:!1,category:"animals_and_nature"},blossom:{keywords:["nature","flowers","yellow"],char:'\u{1f33c}',fitzpatrick_scale:!1,category:"animals_and_nature"},cherry_blossom:{keywords:["nature","plant","spring","flower"],char:'\u{1f338}',fitzpatrick_scale:!1,category:"animals_and_nature"},bouquet:{keywords:["flowers","nature","spring"],char:'\u{1f490}',fitzpatrick_scale:!1,category:"animals_and_nature"},mushroom:{keywords:["plant","vegetable"],char:'\u{1f344}',fitzpatrick_scale:!1,category:"animals_and_nature"},chestnut:{keywords:["food","squirrel"],char:'\u{1f330}',fitzpatrick_scale:!1,category:"animals_and_nature"},jack_o_lantern:{keywords:["halloween","light","pumpkin","creepy","fall"],char:'\u{1f383}',fitzpatrick_scale:!1,category:"animals_and_nature"},shell:{keywords:["nature","sea","beach"],char:'\u{1f41a}',fitzpatrick_scale:!1,category:"animals_and_nature"},spider_web:{keywords:["animal","insect","arachnid","silk"],char:'\u{1f578}',fitzpatrick_scale:!1,category:"animals_and_nature"},earth_americas:{keywords:["globe","world","USA","international"],char:'\u{1f30e}',fitzpatrick_scale:!1,category:"animals_and_nature"},earth_africa:{keywords:["globe","world","international"],char:'\u{1f30d}',fitzpatrick_scale:!1,category:"animals_and_nature"},earth_asia:{keywords:["globe","world","east","international"],char:'\u{1f30f}',fitzpatrick_scale:!1,category:"animals_and_nature"},full_moon:{keywords:["nature","yellow","twilight","planet","space","night","evening","sleep"],char:'\u{1f315}',fitzpatrick_scale:!1,category:"animals_and_nature"},waning_gibbous_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep","waxing_gibbous_moon"],char:'\u{1f316}',fitzpatrick_scale:!1,category:"animals_and_nature"},last_quarter_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:'\u{1f317}',fitzpatrick_scale:!1,category:"animals_and_nature"},waning_crescent_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:'\u{1f318}',fitzpatrick_scale:!1,category:"animals_and_nature"},new_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:'\u{1f311}',fitzpatrick_scale:!1,category:"animals_and_nature"},waxing_crescent_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:'\u{1f312}',fitzpatrick_scale:!1,category:"animals_and_nature"},first_quarter_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:'\u{1f313}',fitzpatrick_scale:!1,category:"animals_and_nature"},waxing_gibbous_moon:{keywords:["nature","night","sky","gray","twilight","planet","space","evening","sleep"],char:'\u{1f314}',fitzpatrick_scale:!1,category:"animals_and_nature"},new_moon_with_face:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:'\u{1f31a}',fitzpatrick_scale:!1,category:"animals_and_nature"},full_moon_with_face:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:'\u{1f31d}',fitzpatrick_scale:!1,category:"animals_and_nature"},first_quarter_moon_with_face:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:'\u{1f31b}',fitzpatrick_scale:!1,category:"animals_and_nature"},last_quarter_moon_with_face:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:'\u{1f31c}',fitzpatrick_scale:!1,category:"animals_and_nature"},sun_with_face:{keywords:["nature","morning","sky"],char:'\u{1f31e}',fitzpatrick_scale:!1,category:"animals_and_nature"},crescent_moon:{keywords:["night","sleep","sky","evening","magic"],char:'\u{1f319}',fitzpatrick_scale:!1,category:"animals_and_nature"},star:{keywords:["night","yellow"],char:'\u2b50',fitzpatrick_scale:!1,category:"animals_and_nature"},star2:{keywords:["night","sparkle","awesome","good","magic"],char:'\u{1f31f}',fitzpatrick_scale:!1,category:"animals_and_nature"},dizzy:{keywords:["star","sparkle","shoot","magic"],char:'\u{1f4ab}',fitzpatrick_scale:!1,category:"animals_and_nature"},sparkles:{keywords:["stars","shine","shiny","cool","awesome","good","magic"],char:'\u2728',fitzpatrick_scale:!1,category:"animals_and_nature"},comet:{keywords:["space"],char:'\u2604',fitzpatrick_scale:!1,category:"animals_and_nature"},sunny:{keywords:["weather","nature","brightness","summer","beach","spring"],char:'\u2600\ufe0f',fitzpatrick_scale:!1,category:"animals_and_nature"},sun_behind_small_cloud:{keywords:["weather"],char:'\u{1f324}',fitzpatrick_scale:!1,category:"animals_and_nature"},partly_sunny:{keywords:["weather","nature","cloudy","morning","fall","spring"],char:'\u26c5',fitzpatrick_scale:!1,category:"animals_and_nature"},sun_behind_large_cloud:{keywords:["weather"],char:'\u{1f325}',fitzpatrick_scale:!1,category:"animals_and_nature"},sun_behind_rain_cloud:{keywords:["weather"],char:'\u{1f326}',fitzpatrick_scale:!1,category:"animals_and_nature"},cloud:{keywords:["weather","sky"],char:'\u2601\ufe0f',fitzpatrick_scale:!1,category:"animals_and_nature"},cloud_with_rain:{keywords:["weather"],char:'\u{1f327}',fitzpatrick_scale:!1,category:"animals_and_nature"},cloud_with_lightning_and_rain:{keywords:["weather","lightning"],char:'\u26c8',fitzpatrick_scale:!1,category:"animals_and_nature"},cloud_with_lightning:{keywords:["weather","thunder"],char:'\u{1f329}',fitzpatrick_scale:!1,category:"animals_and_nature"},zap:{keywords:["thunder","weather","lightning bolt","fast"],char:'\u26a1',fitzpatrick_scale:!1,category:"animals_and_nature"},fire:{keywords:["hot","cook","flame"],char:'\u{1f525}',fitzpatrick_scale:!1,category:"animals_and_nature"},boom:{keywords:["bomb","explode","explosion","collision","blown"],char:'\u{1f4a5}',fitzpatrick_scale:!1,category:"animals_and_nature"},snowflake:{keywords:["winter","season","cold","weather","christmas","xmas"],char:'\u2744\ufe0f',fitzpatrick_scale:!1,category:"animals_and_nature"},cloud_with_snow:{keywords:["weather"],char:'\u{1f328}',fitzpatrick_scale:!1,category:"animals_and_nature"},snowman:{keywords:["winter","season","cold","weather","christmas","xmas","frozen","without_snow"],char:'\u26c4',fitzpatrick_scale:!1,category:"animals_and_nature"},snowman_with_snow:{keywords:["winter","season","cold","weather","christmas","xmas","frozen"],char:'\u2603',fitzpatrick_scale:!1,category:"animals_and_nature"},wind_face:{keywords:["gust","air"],char:'\u{1f32c}',fitzpatrick_scale:!1,category:"animals_and_nature"},dash:{keywords:["wind","air","fast","shoo","fart","smoke","puff"],char:'\u{1f4a8}',fitzpatrick_scale:!1,category:"animals_and_nature"},tornado:{keywords:["weather","cyclone","twister"],char:'\u{1f32a}',fitzpatrick_scale:!1,category:"animals_and_nature"},fog:{keywords:["weather"],char:'\u{1f32b}',fitzpatrick_scale:!1,category:"animals_and_nature"},open_umbrella:{keywords:["weather","spring"],char:'\u2602',fitzpatrick_scale:!1,category:"animals_and_nature"},umbrella:{keywords:["rainy","weather","spring"],char:'\u2614',fitzpatrick_scale:!1,category:"animals_and_nature"},droplet:{keywords:["water","drip","faucet","spring"],char:'\u{1f4a7}',fitzpatrick_scale:!1,category:"animals_and_nature"},sweat_drops:{keywords:["water","drip","oops"],char:'\u{1f4a6}',fitzpatrick_scale:!1,category:"animals_and_nature"},ocean:{keywords:["sea","water","wave","nature","tsunami","disaster"],char:'\u{1f30a}',fitzpatrick_scale:!1,category:"animals_and_nature"},green_apple:{keywords:["fruit","nature"],char:'\u{1f34f}',fitzpatrick_scale:!1,category:"food_and_drink"},apple:{keywords:["fruit","mac","school"],char:'\u{1f34e}',fitzpatrick_scale:!1,category:"food_and_drink"},pear:{keywords:["fruit","nature","food"],char:'\u{1f350}',fitzpatrick_scale:!1,category:"food_and_drink"},tangerine:{keywords:["food","fruit","nature","orange"],char:'\u{1f34a}',fitzpatrick_scale:!1,category:"food_and_drink"},lemon:{keywords:["fruit","nature"],char:'\u{1f34b}',fitzpatrick_scale:!1,category:"food_and_drink"},banana:{keywords:["fruit","food","monkey"],char:'\u{1f34c}',fitzpatrick_scale:!1,category:"food_and_drink"},watermelon:{keywords:["fruit","food","picnic","summer"],char:'\u{1f349}',fitzpatrick_scale:!1,category:"food_and_drink"},grapes:{keywords:["fruit","food","wine"],char:'\u{1f347}',fitzpatrick_scale:!1,category:"food_and_drink"},strawberry:{keywords:["fruit","food","nature"],char:'\u{1f353}',fitzpatrick_scale:!1,category:"food_and_drink"},melon:{keywords:["fruit","nature","food"],char:'\u{1f348}',fitzpatrick_scale:!1,category:"food_and_drink"},cherries:{keywords:["food","fruit"],char:'\u{1f352}',fitzpatrick_scale:!1,category:"food_and_drink"},peach:{keywords:["fruit","nature","food"],char:'\u{1f351}',fitzpatrick_scale:!1,category:"food_and_drink"},pineapple:{keywords:["fruit","nature","food"],char:'\u{1f34d}',fitzpatrick_scale:!1,category:"food_and_drink"},coconut:{keywords:["fruit","nature","food","palm"],char:'\u{1f965}',fitzpatrick_scale:!1,category:"food_and_drink"},kiwi_fruit:{keywords:["fruit","food"],char:'\u{1f95d}',fitzpatrick_scale:!1,category:"food_and_drink"},mango:{keywords:["fruit","food","tropical"],char:'\u{1f96d}',fitzpatrick_scale:!1,category:"food_and_drink"},avocado:{keywords:["fruit","food"],char:'\u{1f951}',fitzpatrick_scale:!1,category:"food_and_drink"},broccoli:{keywords:["fruit","food","vegetable"],char:'\u{1f966}',fitzpatrick_scale:!1,category:"food_and_drink"},tomato:{keywords:["fruit","vegetable","nature","food"],char:'\u{1f345}',fitzpatrick_scale:!1,category:"food_and_drink"},eggplant:{keywords:["vegetable","nature","food","aubergine"],char:'\u{1f346}',fitzpatrick_scale:!1,category:"food_and_drink"},cucumber:{keywords:["fruit","food","pickle"],char:'\u{1f952}',fitzpatrick_scale:!1,category:"food_and_drink"},carrot:{keywords:["vegetable","food","orange"],char:'\u{1f955}',fitzpatrick_scale:!1,category:"food_and_drink"},hot_pepper:{keywords:["food","spicy","chilli","chili"],char:'\u{1f336}',fitzpatrick_scale:!1,category:"food_and_drink"},potato:{keywords:["food","tuber","vegatable","starch"],char:'\u{1f954}',fitzpatrick_scale:!1,category:"food_and_drink"},corn:{keywords:["food","vegetable","plant"],char:'\u{1f33d}',fitzpatrick_scale:!1,category:"food_and_drink"},leafy_greens:{keywords:["food","vegetable","plant","bok choy","cabbage","kale","lettuce"],char:'\u{1f96c}',fitzpatrick_scale:!1,category:"food_and_drink"},sweet_potato:{keywords:["food","nature"],char:'\u{1f360}',fitzpatrick_scale:!1,category:"food_and_drink"},peanuts:{keywords:["food","nut"],char:'\u{1f95c}',fitzpatrick_scale:!1,category:"food_and_drink"},honey_pot:{keywords:["bees","sweet","kitchen"],char:'\u{1f36f}',fitzpatrick_scale:!1,category:"food_and_drink"},croissant:{keywords:["food","bread","french"],char:'\u{1f950}',fitzpatrick_scale:!1,category:"food_and_drink"},bread:{keywords:["food","wheat","breakfast","toast"],char:'\u{1f35e}',fitzpatrick_scale:!1,category:"food_and_drink"},baguette_bread:{keywords:["food","bread","french"],char:'\u{1f956}',fitzpatrick_scale:!1,category:"food_and_drink"},bagel:{keywords:["food","bread","bakery","schmear"],char:'\u{1f96f}',fitzpatrick_scale:!1,category:"food_and_drink"},pretzel:{keywords:["food","bread","twisted"],char:'\u{1f968}',fitzpatrick_scale:!1,category:"food_and_drink"},cheese:{keywords:["food","chadder"],char:'\u{1f9c0}',fitzpatrick_scale:!1,category:"food_and_drink"},egg:{keywords:["food","chicken","breakfast"],char:'\u{1f95a}',fitzpatrick_scale:!1,category:"food_and_drink"},bacon:{keywords:["food","breakfast","pork","pig","meat"],char:'\u{1f953}',fitzpatrick_scale:!1,category:"food_and_drink"},steak:{keywords:["food","cow","meat","cut","chop","lambchop","porkchop"],char:'\u{1f969}',fitzpatrick_scale:!1,category:"food_and_drink"},pancakes:{keywords:["food","breakfast","flapjacks","hotcakes"],char:'\u{1f95e}',fitzpatrick_scale:!1,category:"food_and_drink"},poultry_leg:{keywords:["food","meat","drumstick","bird","chicken","turkey"],char:'\u{1f357}',fitzpatrick_scale:!1,category:"food_and_drink"},meat_on_bone:{keywords:["good","food","drumstick"],char:'\u{1f356}',fitzpatrick_scale:!1,category:"food_and_drink"},bone:{keywords:["skeleton"],char:'\u{1f9b4}',fitzpatrick_scale:!1,category:"food_and_drink"},fried_shrimp:{keywords:["food","animal","appetizer","summer"],char:'\u{1f364}',fitzpatrick_scale:!1,category:"food_and_drink"},fried_egg:{keywords:["food","breakfast","kitchen","egg"],char:'\u{1f373}',fitzpatrick_scale:!1,category:"food_and_drink"},hamburger:{keywords:["meat","fast food","beef","cheeseburger","mcdonalds","burger king"],char:'\u{1f354}',fitzpatrick_scale:!1,category:"food_and_drink"},fries:{keywords:["chips","snack","fast food"],char:'\u{1f35f}',fitzpatrick_scale:!1,category:"food_and_drink"},stuffed_flatbread:{keywords:["food","flatbread","stuffed","gyro"],char:'\u{1f959}',fitzpatrick_scale:!1,category:"food_and_drink"},hotdog:{keywords:["food","frankfurter"],char:'\u{1f32d}',fitzpatrick_scale:!1,category:"food_and_drink"},pizza:{keywords:["food","party"],char:'\u{1f355}',fitzpatrick_scale:!1,category:"food_and_drink"},sandwich:{keywords:["food","lunch","bread"],char:'\u{1f96a}',fitzpatrick_scale:!1,category:"food_and_drink"},canned_food:{keywords:["food","soup"],char:'\u{1f96b}',fitzpatrick_scale:!1,category:"food_and_drink"},spaghetti:{keywords:["food","italian","noodle"],char:'\u{1f35d}',fitzpatrick_scale:!1,category:"food_and_drink"},taco:{keywords:["food","mexican"],char:'\u{1f32e}',fitzpatrick_scale:!1,category:"food_and_drink"},burrito:{keywords:["food","mexican"],char:'\u{1f32f}',fitzpatrick_scale:!1,category:"food_and_drink"},green_salad:{keywords:["food","healthy","lettuce"],char:'\u{1f957}',fitzpatrick_scale:!1,category:"food_and_drink"},shallow_pan_of_food:{keywords:["food","cooking","casserole","paella"],char:'\u{1f958}',fitzpatrick_scale:!1,category:"food_and_drink"},ramen:{keywords:["food","japanese","noodle","chopsticks"],char:'\u{1f35c}',fitzpatrick_scale:!1,category:"food_and_drink"},stew:{keywords:["food","meat","soup"],char:'\u{1f372}',fitzpatrick_scale:!1,category:"food_and_drink"},fish_cake:{keywords:["food","japan","sea","beach","narutomaki","pink","swirl","kamaboko","surimi","ramen"],char:'\u{1f365}',fitzpatrick_scale:!1,category:"food_and_drink"},fortune_cookie:{keywords:["food","prophecy"],char:'\u{1f960}',fitzpatrick_scale:!1,category:"food_and_drink"},sushi:{keywords:["food","fish","japanese","rice"],char:'\u{1f363}',fitzpatrick_scale:!1,category:"food_and_drink"},bento:{keywords:["food","japanese","box"],char:'\u{1f371}',fitzpatrick_scale:!1,category:"food_and_drink"},curry:{keywords:["food","spicy","hot","indian"],char:'\u{1f35b}',fitzpatrick_scale:!1,category:"food_and_drink"},rice_ball:{keywords:["food","japanese"],char:'\u{1f359}',fitzpatrick_scale:!1,category:"food_and_drink"},rice:{keywords:["food","china","asian"],char:'\u{1f35a}',fitzpatrick_scale:!1,category:"food_and_drink"},rice_cracker:{keywords:["food","japanese"],char:'\u{1f358}',fitzpatrick_scale:!1,category:"food_and_drink"},oden:{keywords:["food","japanese"],char:'\u{1f362}',fitzpatrick_scale:!1,category:"food_and_drink"},dango:{keywords:["food","dessert","sweet","japanese","barbecue","meat"],char:'\u{1f361}',fitzpatrick_scale:!1,category:"food_and_drink"},shaved_ice:{keywords:["hot","dessert","summer"],char:'\u{1f367}',fitzpatrick_scale:!1,category:"food_and_drink"},ice_cream:{keywords:["food","hot","dessert"],char:'\u{1f368}',fitzpatrick_scale:!1,category:"food_and_drink"},icecream:{keywords:["food","hot","dessert","summer"],char:'\u{1f366}',fitzpatrick_scale:!1,category:"food_and_drink"},pie:{keywords:["food","dessert","pastry"],char:'\u{1f967}',fitzpatrick_scale:!1,category:"food_and_drink"},cake:{keywords:["food","dessert"],char:'\u{1f370}',fitzpatrick_scale:!1,category:"food_and_drink"},cupcake:{keywords:["food","dessert","bakery","sweet"],char:'\u{1f9c1}',fitzpatrick_scale:!1,category:"food_and_drink"},moon_cake:{keywords:["food","autumn"],char:'\u{1f96e}',fitzpatrick_scale:!1,category:"food_and_drink"},birthday:{keywords:["food","dessert","cake"],char:'\u{1f382}',fitzpatrick_scale:!1,category:"food_and_drink"},custard:{keywords:["dessert","food"],char:'\u{1f36e}',fitzpatrick_scale:!1,category:"food_and_drink"},candy:{keywords:["snack","dessert","sweet","lolly"],char:'\u{1f36c}',fitzpatrick_scale:!1,category:"food_and_drink"},lollipop:{keywords:["food","snack","candy","sweet"],char:'\u{1f36d}',fitzpatrick_scale:!1,category:"food_and_drink"},chocolate_bar:{keywords:["food","snack","dessert","sweet"],char:'\u{1f36b}',fitzpatrick_scale:!1,category:"food_and_drink"},popcorn:{keywords:["food","movie theater","films","snack"],char:'\u{1f37f}',fitzpatrick_scale:!1,category:"food_and_drink"},dumpling:{keywords:["food","empanada","pierogi","potsticker"],char:'\u{1f95f}',fitzpatrick_scale:!1,category:"food_and_drink"},doughnut:{keywords:["food","dessert","snack","sweet","donut"],char:'\u{1f369}',fitzpatrick_scale:!1,category:"food_and_drink"},cookie:{keywords:["food","snack","oreo","chocolate","sweet","dessert"],char:'\u{1f36a}',fitzpatrick_scale:!1,category:"food_and_drink"},milk_glass:{keywords:["beverage","drink","cow"],char:'\u{1f95b}',fitzpatrick_scale:!1,category:"food_and_drink"},beer:{keywords:["relax","beverage","drink","drunk","party","pub","summer","alcohol","booze"],char:'\u{1f37a}',fitzpatrick_scale:!1,category:"food_and_drink"},beers:{keywords:["relax","beverage","drink","drunk","party","pub","summer","alcohol","booze"],char:'\u{1f37b}',fitzpatrick_scale:!1,category:"food_and_drink"},clinking_glasses:{keywords:["beverage","drink","party","alcohol","celebrate","cheers","wine","champagne","toast"],char:'\u{1f942}',fitzpatrick_scale:!1,category:"food_and_drink"},wine_glass:{keywords:["drink","beverage","drunk","alcohol","booze"],char:'\u{1f377}',fitzpatrick_scale:!1,category:"food_and_drink"},tumbler_glass:{keywords:["drink","beverage","drunk","alcohol","liquor","booze","bourbon","scotch","whisky","glass","shot"],char:'\u{1f943}',fitzpatrick_scale:!1,category:"food_and_drink"},cocktail:{keywords:["drink","drunk","alcohol","beverage","booze","mojito"],char:'\u{1f378}',fitzpatrick_scale:!1,category:"food_and_drink"},tropical_drink:{keywords:["beverage","cocktail","summer","beach","alcohol","booze","mojito"],char:'\u{1f379}',fitzpatrick_scale:!1,category:"food_and_drink"},champagne:{keywords:["drink","wine","bottle","celebration"],char:'\u{1f37e}',fitzpatrick_scale:!1,category:"food_and_drink"},sake:{keywords:["wine","drink","drunk","beverage","japanese","alcohol","booze"],char:'\u{1f376}',fitzpatrick_scale:!1,category:"food_and_drink"},tea:{keywords:["drink","bowl","breakfast","green","british"],char:'\u{1f375}',fitzpatrick_scale:!1,category:"food_and_drink"},cup_with_straw:{keywords:["drink","soda"],char:'\u{1f964}',fitzpatrick_scale:!1,category:"food_and_drink"},coffee:{keywords:["beverage","caffeine","latte","espresso"],char:'\u2615',fitzpatrick_scale:!1,category:"food_and_drink"},baby_bottle:{keywords:["food","container","milk"],char:'\u{1f37c}',fitzpatrick_scale:!1,category:"food_and_drink"},salt:{keywords:["condiment","shaker"],char:'\u{1f9c2}',fitzpatrick_scale:!1,category:"food_and_drink"},spoon:{keywords:["cutlery","kitchen","tableware"],char:'\u{1f944}',fitzpatrick_scale:!1,category:"food_and_drink"},fork_and_knife:{keywords:["cutlery","kitchen"],char:'\u{1f374}',fitzpatrick_scale:!1,category:"food_and_drink"},plate_with_cutlery:{keywords:["food","eat","meal","lunch","dinner","restaurant"],char:'\u{1f37d}',fitzpatrick_scale:!1,category:"food_and_drink"},bowl_with_spoon:{keywords:["food","breakfast","cereal","oatmeal","porridge"],char:'\u{1f963}',fitzpatrick_scale:!1,category:"food_and_drink"},takeout_box:{keywords:["food","leftovers"],char:'\u{1f961}',fitzpatrick_scale:!1,category:"food_and_drink"},chopsticks:{keywords:["food"],char:'\u{1f962}',fitzpatrick_scale:!1,category:"food_and_drink"},soccer:{keywords:["sports","football"],char:'\u26bd',fitzpatrick_scale:!1,category:"activity"},basketball:{keywords:["sports","balls","NBA"],char:'\u{1f3c0}',fitzpatrick_scale:!1,category:"activity"},football:{keywords:["sports","balls","NFL"],char:'\u{1f3c8}',fitzpatrick_scale:!1,category:"activity"},baseball:{keywords:["sports","balls"],char:'\u26be',fitzpatrick_scale:!1,category:"activity"},softball:{keywords:["sports","balls"],char:'\u{1f94e}',fitzpatrick_scale:!1,category:"activity"},tennis:{keywords:["sports","balls","green"],char:'\u{1f3be}',fitzpatrick_scale:!1,category:"activity"},volleyball:{keywords:["sports","balls"],char:'\u{1f3d0}',fitzpatrick_scale:!1,category:"activity"},rugby_football:{keywords:["sports","team"],char:'\u{1f3c9}',fitzpatrick_scale:!1,category:"activity"},flying_disc:{keywords:["sports","frisbee","ultimate"],char:'\u{1f94f}',fitzpatrick_scale:!1,category:"activity"},"8ball":{keywords:["pool","hobby","game","luck","magic"],char:'\u{1f3b1}',fitzpatrick_scale:!1,category:"activity"},golf:{keywords:["sports","business","flag","hole","summer"],char:'\u26f3',fitzpatrick_scale:!1,category:"activity"},golfing_woman:{keywords:["sports","business","woman","female"],char:'\u{1f3cc}\ufe0f\u200d\u2640\ufe0f',fitzpatrick_scale:!1,category:"activity"},golfing_man:{keywords:["sports","business"],char:'\u{1f3cc}',fitzpatrick_scale:!0,category:"activity"},ping_pong:{keywords:["sports","pingpong"],char:'\u{1f3d3}',fitzpatrick_scale:!1,category:"activity"},badminton:{keywords:["sports"],char:'\u{1f3f8}',fitzpatrick_scale:!1,category:"activity"},goal_net:{keywords:["sports"],char:'\u{1f945}',fitzpatrick_scale:!1,category:"activity"},ice_hockey:{keywords:["sports"],char:'\u{1f3d2}',fitzpatrick_scale:!1,category:"activity"},field_hockey:{keywords:["sports"],char:'\u{1f3d1}',fitzpatrick_scale:!1,category:"activity"},lacrosse:{keywords:["sports","ball","stick"],char:'\u{1f94d}',fitzpatrick_scale:!1,category:"activity"},cricket:{keywords:["sports"],char:'\u{1f3cf}',fitzpatrick_scale:!1,category:"activity"},ski:{keywords:["sports","winter","cold","snow"],char:'\u{1f3bf}',fitzpatrick_scale:!1,category:"activity"},skier:{keywords:["sports","winter","snow"],char:'\u26f7',fitzpatrick_scale:!1,category:"activity"},snowboarder:{keywords:["sports","winter"],char:'\u{1f3c2}',fitzpatrick_scale:!0,category:"activity"},person_fencing:{keywords:["sports","fencing","sword"],char:'\u{1f93a}',fitzpatrick_scale:!1,category:"activity"},women_wrestling:{keywords:["sports","wrestlers"],char:'\u{1f93c}\u200d\u2640\ufe0f',fitzpatrick_scale:!1,category:"activity"},men_wrestling:{keywords:["sports","wrestlers"],char:'\u{1f93c}\u200d\u2642\ufe0f',fitzpatrick_scale:!1,category:"activity"},woman_cartwheeling:{keywords:["gymnastics"],char:'\u{1f938}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"activity"},man_cartwheeling:{keywords:["gymnastics"],char:'\u{1f938}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"activity"},woman_playing_handball:{keywords:["sports"],char:'\u{1f93e}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"activity"},man_playing_handball:{keywords:["sports"],char:'\u{1f93e}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"activity"},ice_skate:{keywords:["sports"],char:'\u26f8',fitzpatrick_scale:!1,category:"activity"},curling_stone:{keywords:["sports"],char:'\u{1f94c}',fitzpatrick_scale:!1,category:"activity"},skateboard:{keywords:["board"],char:'\u{1f6f9}',fitzpatrick_scale:!1,category:"activity"},sled:{keywords:["sleigh","luge","toboggan"],char:'\u{1f6f7}',fitzpatrick_scale:!1,category:"activity"},bow_and_arrow:{keywords:["sports"],char:'\u{1f3f9}',fitzpatrick_scale:!1,category:"activity"},fishing_pole_and_fish:{keywords:["food","hobby","summer"],char:'\u{1f3a3}',fitzpatrick_scale:!1,category:"activity"},boxing_glove:{keywords:["sports","fighting"],char:'\u{1f94a}',fitzpatrick_scale:!1,category:"activity"},martial_arts_uniform:{keywords:["judo","karate","taekwondo"],char:'\u{1f94b}',fitzpatrick_scale:!1,category:"activity"},rowing_woman:{keywords:["sports","hobby","water","ship","woman","female"],char:'\u{1f6a3}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"activity"},rowing_man:{keywords:["sports","hobby","water","ship"],char:'\u{1f6a3}',fitzpatrick_scale:!0,category:"activity"},climbing_woman:{keywords:["sports","hobby","woman","female","rock"],char:'\u{1f9d7}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"activity"},climbing_man:{keywords:["sports","hobby","man","male","rock"],char:'\u{1f9d7}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"activity"},swimming_woman:{keywords:["sports","exercise","human","athlete","water","summer","woman","female"],char:'\u{1f3ca}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"activity"},swimming_man:{keywords:["sports","exercise","human","athlete","water","summer"],char:'\u{1f3ca}',fitzpatrick_scale:!0,category:"activity"},woman_playing_water_polo:{keywords:["sports","pool"],char:'\u{1f93d}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"activity"},man_playing_water_polo:{keywords:["sports","pool"],char:'\u{1f93d}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"activity"},woman_in_lotus_position:{keywords:["woman","female","meditation","yoga","serenity","zen","mindfulness"],char:'\u{1f9d8}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"activity"},man_in_lotus_position:{keywords:["man","male","meditation","yoga","serenity","zen","mindfulness"],char:'\u{1f9d8}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"activity"},surfing_woman:{keywords:["sports","ocean","sea","summer","beach","woman","female"],char:'\u{1f3c4}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"activity"},surfing_man:{keywords:["sports","ocean","sea","summer","beach"],char:'\u{1f3c4}',fitzpatrick_scale:!0,category:"activity"},bath:{keywords:["clean","shower","bathroom"],char:'\u{1f6c0}',fitzpatrick_scale:!0,category:"activity"},basketball_woman:{keywords:["sports","human","woman","female"],char:'\u26f9\ufe0f\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"activity"},basketball_man:{keywords:["sports","human"],char:'\u26f9',fitzpatrick_scale:!0,category:"activity"},weight_lifting_woman:{keywords:["sports","training","exercise","woman","female"],char:'\u{1f3cb}\ufe0f\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"activity"},weight_lifting_man:{keywords:["sports","training","exercise"],char:'\u{1f3cb}',fitzpatrick_scale:!0,category:"activity"},biking_woman:{keywords:["sports","bike","exercise","hipster","woman","female"],char:'\u{1f6b4}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"activity"},biking_man:{keywords:["sports","bike","exercise","hipster"],char:'\u{1f6b4}',fitzpatrick_scale:!0,category:"activity"},mountain_biking_woman:{keywords:["transportation","sports","human","race","bike","woman","female"],char:'\u{1f6b5}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"activity"},mountain_biking_man:{keywords:["transportation","sports","human","race","bike"],char:'\u{1f6b5}',fitzpatrick_scale:!0,category:"activity"},horse_racing:{keywords:["animal","betting","competition","gambling","luck"],char:'\u{1f3c7}',fitzpatrick_scale:!0,category:"activity"},business_suit_levitating:{keywords:["suit","business","levitate","hover","jump"],char:'\u{1f574}',fitzpatrick_scale:!0,category:"activity"},trophy:{keywords:["win","award","contest","place","ftw","ceremony"],char:'\u{1f3c6}',fitzpatrick_scale:!1,category:"activity"},running_shirt_with_sash:{keywords:["play","pageant"],char:'\u{1f3bd}',fitzpatrick_scale:!1,category:"activity"},medal_sports:{keywords:["award","winning"],char:'\u{1f3c5}',fitzpatrick_scale:!1,category:"activity"},medal_military:{keywords:["award","winning","army"],char:'\u{1f396}',fitzpatrick_scale:!1,category:"activity"},"1st_place_medal":{keywords:["award","winning","first"],char:'\u{1f947}',fitzpatrick_scale:!1,category:"activity"},"2nd_place_medal":{keywords:["award","second"],char:'\u{1f948}',fitzpatrick_scale:!1,category:"activity"},"3rd_place_medal":{keywords:["award","third"],char:'\u{1f949}',fitzpatrick_scale:!1,category:"activity"},reminder_ribbon:{keywords:["sports","cause","support","awareness"],char:'\u{1f397}',fitzpatrick_scale:!1,category:"activity"},rosette:{keywords:["flower","decoration","military"],char:'\u{1f3f5}',fitzpatrick_scale:!1,category:"activity"},ticket:{keywords:["event","concert","pass"],char:'\u{1f3ab}',fitzpatrick_scale:!1,category:"activity"},tickets:{keywords:["sports","concert","entrance"],char:'\u{1f39f}',fitzpatrick_scale:!1,category:"activity"},performing_arts:{keywords:["acting","theater","drama"],char:'\u{1f3ad}',fitzpatrick_scale:!1,category:"activity"},art:{keywords:["design","paint","draw","colors"],char:'\u{1f3a8}',fitzpatrick_scale:!1,category:"activity"},circus_tent:{keywords:["festival","carnival","party"],char:'\u{1f3aa}',fitzpatrick_scale:!1,category:"activity"},woman_juggling:{keywords:["juggle","balance","skill","multitask"],char:'\u{1f939}\u200d\u2640\ufe0f',fitzpatrick_scale:!0,category:"activity"},man_juggling:{keywords:["juggle","balance","skill","multitask"],char:'\u{1f939}\u200d\u2642\ufe0f',fitzpatrick_scale:!0,category:"activity"},microphone:{keywords:["sound","music","PA","sing","talkshow"],char:'\u{1f3a4}',fitzpatrick_scale:!1,category:"activity"},headphones:{keywords:["music","score","gadgets"],char:'\u{1f3a7}',fitzpatrick_scale:!1,category:"activity"},musical_score:{keywords:["treble","clef","compose"],char:'\u{1f3bc}',fitzpatrick_scale:!1,category:"activity"},musical_keyboard:{keywords:["piano","instrument","compose"],char:'\u{1f3b9}',fitzpatrick_scale:!1,category:"activity"},drum:{keywords:["music","instrument","drumsticks","snare"],char:'\u{1f941}',fitzpatrick_scale:!1,category:"activity"},saxophone:{keywords:["music","instrument","jazz","blues"],char:'\u{1f3b7}',fitzpatrick_scale:!1,category:"activity"},trumpet:{keywords:["music","brass"],char:'\u{1f3ba}',fitzpatrick_scale:!1,category:"activity"},guitar:{keywords:["music","instrument"],char:'\u{1f3b8}',fitzpatrick_scale:!1,category:"activity"},violin:{keywords:["music","instrument","orchestra","symphony"],char:'\u{1f3bb}',fitzpatrick_scale:!1,category:"activity"},clapper:{keywords:["movie","film","record"],char:'\u{1f3ac}',fitzpatrick_scale:!1,category:"activity"},video_game:{keywords:["play","console","PS4","controller"],char:'\u{1f3ae}',fitzpatrick_scale:!1,category:"activity"},space_invader:{keywords:["game","arcade","play"],char:'\u{1f47e}',fitzpatrick_scale:!1,category:"activity"},dart:{keywords:["game","play","bar","target","bullseye"],char:'\u{1f3af}',fitzpatrick_scale:!1,category:"activity"},game_die:{keywords:["dice","random","tabletop","play","luck"],char:'\u{1f3b2}',fitzpatrick_scale:!1,category:"activity"},chess_pawn:{keywords:["expendable"],char:"\u265f",fitzpatrick_scale:!1,category:"activity"},slot_machine:{keywords:["bet","gamble","vegas","fruit machine","luck","casino"],char:'\u{1f3b0}',fitzpatrick_scale:!1,category:"activity"},jigsaw:{keywords:["interlocking","puzzle","piece"],char:'\u{1f9e9}',fitzpatrick_scale:!1,category:"activity"},bowling:{keywords:["sports","fun","play"],char:'\u{1f3b3}',fitzpatrick_scale:!1,category:"activity"},red_car:{keywords:["red","transportation","vehicle"],char:'\u{1f697}',fitzpatrick_scale:!1,category:"travel_and_places"},taxi:{keywords:["uber","vehicle","cars","transportation"],char:'\u{1f695}',fitzpatrick_scale:!1,category:"travel_and_places"},blue_car:{keywords:["transportation","vehicle"],char:'\u{1f699}',fitzpatrick_scale:!1,category:"travel_and_places"},bus:{keywords:["car","vehicle","transportation"],char:'\u{1f68c}',fitzpatrick_scale:!1,category:"travel_and_places"},trolleybus:{keywords:["bart","transportation","vehicle"],char:'\u{1f68e}',fitzpatrick_scale:!1,category:"travel_and_places"},racing_car:{keywords:["sports","race","fast","formula","f1"],char:'\u{1f3ce}',fitzpatrick_scale:!1,category:"travel_and_places"},police_car:{keywords:["vehicle","cars","transportation","law","legal","enforcement"],char:'\u{1f693}',fitzpatrick_scale:!1,category:"travel_and_places"},ambulance:{keywords:["health","911","hospital"],char:'\u{1f691}',fitzpatrick_scale:!1,category:"travel_and_places"},fire_engine:{keywords:["transportation","cars","vehicle"],char:'\u{1f692}',fitzpatrick_scale:!1,category:"travel_and_places"},minibus:{keywords:["vehicle","car","transportation"],char:'\u{1f690}',fitzpatrick_scale:!1,category:"travel_and_places"},truck:{keywords:["cars","transportation"],char:'\u{1f69a}',fitzpatrick_scale:!1,category:"travel_and_places"},articulated_lorry:{keywords:["vehicle","cars","transportation","express"],char:'\u{1f69b}',fitzpatrick_scale:!1,category:"travel_and_places"},tractor:{keywords:["vehicle","car","farming","agriculture"],char:'\u{1f69c}',fitzpatrick_scale:!1,category:"travel_and_places"},kick_scooter:{keywords:["vehicle","kick","razor"],char:'\u{1f6f4}',fitzpatrick_scale:!1,category:"travel_and_places"},motorcycle:{keywords:["race","sports","fast"],char:'\u{1f3cd}',fitzpatrick_scale:!1,category:"travel_and_places"},bike:{keywords:["sports","bicycle","exercise","hipster"],char:'\u{1f6b2}',fitzpatrick_scale:!1,category:"travel_and_places"},motor_scooter:{keywords:["vehicle","vespa","sasha"],char:'\u{1f6f5}',fitzpatrick_scale:!1,category:"travel_and_places"},rotating_light:{keywords:["police","ambulance","911","emergency","alert","error","pinged","law","legal"],char:'\u{1f6a8}',fitzpatrick_scale:!1,category:"travel_and_places"},oncoming_police_car:{keywords:["vehicle","law","legal","enforcement","911"],char:'\u{1f694}',fitzpatrick_scale:!1,category:"travel_and_places"},oncoming_bus:{keywords:["vehicle","transportation"],char:'\u{1f68d}',fitzpatrick_scale:!1,category:"travel_and_places"},oncoming_automobile:{keywords:["car","vehicle","transportation"],char:'\u{1f698}',fitzpatrick_scale:!1,category:"travel_and_places"},oncoming_taxi:{keywords:["vehicle","cars","uber"],char:'\u{1f696}',fitzpatrick_scale:!1,category:"travel_and_places"},aerial_tramway:{keywords:["transportation","vehicle","ski"],char:'\u{1f6a1}',fitzpatrick_scale:!1,category:"travel_and_places"},mountain_cableway:{keywords:["transportation","vehicle","ski"],char:'\u{1f6a0}',fitzpatrick_scale:!1,category:"travel_and_places"},suspension_railway:{keywords:["vehicle","transportation"],char:'\u{1f69f}',fitzpatrick_scale:!1,category:"travel_and_places"},railway_car:{keywords:["transportation","vehicle"],char:'\u{1f683}',fitzpatrick_scale:!1,category:"travel_and_places"},train:{keywords:["transportation","vehicle","carriage","public","travel"],char:'\u{1f68b}',fitzpatrick_scale:!1,category:"travel_and_places"},monorail:{keywords:["transportation","vehicle"],char:'\u{1f69d}',fitzpatrick_scale:!1,category:"travel_and_places"},bullettrain_side:{keywords:["transportation","vehicle"],char:'\u{1f684}',fitzpatrick_scale:!1,category:"travel_and_places"},bullettrain_front:{keywords:["transportation","vehicle","speed","fast","public","travel"],char:'\u{1f685}',fitzpatrick_scale:!1,category:"travel_and_places"},light_rail:{keywords:["transportation","vehicle"],char:'\u{1f688}',fitzpatrick_scale:!1,category:"travel_and_places"},mountain_railway:{keywords:["transportation","vehicle"],char:'\u{1f69e}',fitzpatrick_scale:!1,category:"travel_and_places"},steam_locomotive:{keywords:["transportation","vehicle","train"],char:'\u{1f682}',fitzpatrick_scale:!1,category:"travel_and_places"},train2:{keywords:["transportation","vehicle"],char:'\u{1f686}',fitzpatrick_scale:!1,category:"travel_and_places"},metro:{keywords:["transportation","blue-square","mrt","underground","tube"],char:'\u{1f687}',fitzpatrick_scale:!1,category:"travel_and_places"},tram:{keywords:["transportation","vehicle"],char:'\u{1f68a}',fitzpatrick_scale:!1,category:"travel_and_places"},station:{keywords:["transportation","vehicle","public"],char:'\u{1f689}',fitzpatrick_scale:!1,category:"travel_and_places"},flying_saucer:{keywords:["transportation","vehicle","ufo"],char:'\u{1f6f8}',fitzpatrick_scale:!1,category:"travel_and_places"},helicopter:{keywords:["transportation","vehicle","fly"],char:'\u{1f681}',fitzpatrick_scale:!1,category:"travel_and_places"},small_airplane:{keywords:["flight","transportation","fly","vehicle"],char:'\u{1f6e9}',fitzpatrick_scale:!1,category:"travel_and_places"},airplane:{keywords:["vehicle","transportation","flight","fly"],char:'\u2708\ufe0f',fitzpatrick_scale:!1,category:"travel_and_places"},flight_departure:{keywords:["airport","flight","landing"],char:'\u{1f6eb}',fitzpatrick_scale:!1,category:"travel_and_places"},flight_arrival:{keywords:["airport","flight","boarding"],char:'\u{1f6ec}',fitzpatrick_scale:!1,category:"travel_and_places"},sailboat:{keywords:["ship","summer","transportation","water","sailing"],char:'\u26f5',fitzpatrick_scale:!1,category:"travel_and_places"},motor_boat:{keywords:["ship"],char:'\u{1f6e5}',fitzpatrick_scale:!1,category:"travel_and_places"},speedboat:{keywords:["ship","transportation","vehicle","summer"],char:'\u{1f6a4}',fitzpatrick_scale:!1,category:"travel_and_places"},ferry:{keywords:["boat","ship","yacht"],char:'\u26f4',fitzpatrick_scale:!1,category:"travel_and_places"},passenger_ship:{keywords:["yacht","cruise","ferry"],char:'\u{1f6f3}',fitzpatrick_scale:!1,category:"travel_and_places"},rocket:{keywords:["launch","ship","staffmode","NASA","outer space","outer_space","fly"],char:'\u{1f680}',fitzpatrick_scale:!1,category:"travel_and_places"},artificial_satellite:{keywords:["communication","gps","orbit","spaceflight","NASA","ISS"],char:'\u{1f6f0}',fitzpatrick_scale:!1,category:"travel_and_places"},seat:{keywords:["sit","airplane","transport","bus","flight","fly"],char:'\u{1f4ba}',fitzpatrick_scale:!1,category:"travel_and_places"},canoe:{keywords:["boat","paddle","water","ship"],char:'\u{1f6f6}',fitzpatrick_scale:!1,category:"travel_and_places"},anchor:{keywords:["ship","ferry","sea","boat"],char:'\u2693',fitzpatrick_scale:!1,category:"travel_and_places"},construction:{keywords:["wip","progress","caution","warning"],char:'\u{1f6a7}',fitzpatrick_scale:!1,category:"travel_and_places"},fuelpump:{keywords:["gas station","petroleum"],char:'\u26fd',fitzpatrick_scale:!1,category:"travel_and_places"},busstop:{keywords:["transportation","wait"],char:'\u{1f68f}',fitzpatrick_scale:!1,category:"travel_and_places"},vertical_traffic_light:{keywords:["transportation","driving"],char:'\u{1f6a6}',fitzpatrick_scale:!1,category:"travel_and_places"},traffic_light:{keywords:["transportation","signal"],char:'\u{1f6a5}',fitzpatrick_scale:!1,category:"travel_and_places"},checkered_flag:{keywords:["contest","finishline","race","gokart"],char:'\u{1f3c1}',fitzpatrick_scale:!1,category:"travel_and_places"},ship:{keywords:["transportation","titanic","deploy"],char:'\u{1f6a2}',fitzpatrick_scale:!1,category:"travel_and_places"},ferris_wheel:{keywords:["photo","carnival","londoneye"],char:'\u{1f3a1}',fitzpatrick_scale:!1,category:"travel_and_places"},roller_coaster:{keywords:["carnival","playground","photo","fun"],char:'\u{1f3a2}',fitzpatrick_scale:!1,category:"travel_and_places"},carousel_horse:{keywords:["photo","carnival"],char:'\u{1f3a0}',fitzpatrick_scale:!1,category:"travel_and_places"},building_construction:{keywords:["wip","working","progress"],char:'\u{1f3d7}',fitzpatrick_scale:!1,category:"travel_and_places"},foggy:{keywords:["photo","mountain"],char:'\u{1f301}',fitzpatrick_scale:!1,category:"travel_and_places"},tokyo_tower:{keywords:["photo","japanese"],char:'\u{1f5fc}',fitzpatrick_scale:!1,category:"travel_and_places"},factory:{keywords:["building","industry","pollution","smoke"],char:'\u{1f3ed}',fitzpatrick_scale:!1,category:"travel_and_places"},fountain:{keywords:["photo","summer","water","fresh"],char:'\u26f2',fitzpatrick_scale:!1,category:"travel_and_places"},rice_scene:{keywords:["photo","japan","asia","tsukimi"],char:'\u{1f391}',fitzpatrick_scale:!1,category:"travel_and_places"},mountain:{keywords:["photo","nature","environment"],char:'\u26f0',fitzpatrick_scale:!1,category:"travel_and_places"},mountain_snow:{keywords:["photo","nature","environment","winter","cold"],char:'\u{1f3d4}',fitzpatrick_scale:!1,category:"travel_and_places"},mount_fuji:{keywords:["photo","mountain","nature","japanese"],char:'\u{1f5fb}',fitzpatrick_scale:!1,category:"travel_and_places"},volcano:{keywords:["photo","nature","disaster"],char:'\u{1f30b}',fitzpatrick_scale:!1,category:"travel_and_places"},japan:{keywords:["nation","country","japanese","asia"],char:'\u{1f5fe}',fitzpatrick_scale:!1,category:"travel_and_places"},camping:{keywords:["photo","outdoors","tent"],char:'\u{1f3d5}',fitzpatrick_scale:!1,category:"travel_and_places"},tent:{keywords:["photo","camping","outdoors"],char:'\u26fa',fitzpatrick_scale:!1,category:"travel_and_places"},national_park:{keywords:["photo","environment","nature"],char:'\u{1f3de}',fitzpatrick_scale:!1,category:"travel_and_places"},motorway:{keywords:["road","cupertino","interstate","highway"],char:'\u{1f6e3}',fitzpatrick_scale:!1,category:"travel_and_places"},railway_track:{keywords:["train","transportation"],char:'\u{1f6e4}',fitzpatrick_scale:!1,category:"travel_and_places"},sunrise:{keywords:["morning","view","vacation","photo"],char:'\u{1f305}',fitzpatrick_scale:!1,category:"travel_and_places"},sunrise_over_mountains:{keywords:["view","vacation","photo"],char:'\u{1f304}',fitzpatrick_scale:!1,category:"travel_and_places"},desert:{keywords:["photo","warm","saharah"],char:'\u{1f3dc}',fitzpatrick_scale:!1,category:"travel_and_places"},beach_umbrella:{keywords:["weather","summer","sunny","sand","mojito"],char:'\u{1f3d6}',fitzpatrick_scale:!1,category:"travel_and_places"},desert_island:{keywords:["photo","tropical","mojito"],char:'\u{1f3dd}',fitzpatrick_scale:!1,category:"travel_and_places"},city_sunrise:{keywords:["photo","good morning","dawn"],char:'\u{1f307}',fitzpatrick_scale:!1,category:"travel_and_places"},city_sunset:{keywords:["photo","evening","sky","buildings"],char:'\u{1f306}',fitzpatrick_scale:!1,category:"travel_and_places"},cityscape:{keywords:["photo","night life","urban"],char:'\u{1f3d9}',fitzpatrick_scale:!1,category:"travel_and_places"},night_with_stars:{keywords:["evening","city","downtown"],char:'\u{1f303}',fitzpatrick_scale:!1,category:"travel_and_places"},bridge_at_night:{keywords:["photo","sanfrancisco"],char:'\u{1f309}',fitzpatrick_scale:!1,category:"travel_and_places"},milky_way:{keywords:["photo","space","stars"],char:'\u{1f30c}',fitzpatrick_scale:!1,category:"travel_and_places"},stars:{keywords:["night","photo"],char:'\u{1f320}',fitzpatrick_scale:!1,category:"travel_and_places"},sparkler:{keywords:["stars","night","shine"],char:'\u{1f387}',fitzpatrick_scale:!1,category:"travel_and_places"},fireworks:{keywords:["photo","festival","carnival","congratulations"],char:'\u{1f386}',fitzpatrick_scale:!1,category:"travel_and_places"},rainbow:{keywords:["nature","happy","unicorn_face","photo","sky","spring"],char:'\u{1f308}',fitzpatrick_scale:!1,category:"travel_and_places"},houses:{keywords:["buildings","photo"],char:'\u{1f3d8}',fitzpatrick_scale:!1,category:"travel_and_places"},european_castle:{keywords:["building","royalty","history"],char:'\u{1f3f0}',fitzpatrick_scale:!1,category:"travel_and_places"},japanese_castle:{keywords:["photo","building"],char:'\u{1f3ef}',fitzpatrick_scale:!1,category:"travel_and_places"},stadium:{keywords:["photo","place","sports","concert","venue"],char:'\u{1f3df}',fitzpatrick_scale:!1,category:"travel_and_places"},statue_of_liberty:{keywords:["american","newyork"],char:'\u{1f5fd}',fitzpatrick_scale:!1,category:"travel_and_places"},house:{keywords:["building","home"],char:'\u{1f3e0}',fitzpatrick_scale:!1,category:"travel_and_places"},house_with_garden:{keywords:["home","plant","nature"],char:'\u{1f3e1}',fitzpatrick_scale:!1,category:"travel_and_places"},derelict_house:{keywords:["abandon","evict","broken","building"],char:'\u{1f3da}',fitzpatrick_scale:!1,category:"travel_and_places"},office:{keywords:["building","bureau","work"],char:'\u{1f3e2}',fitzpatrick_scale:!1,category:"travel_and_places"},department_store:{keywords:["building","shopping","mall"],char:'\u{1f3ec}',fitzpatrick_scale:!1,category:"travel_and_places"},post_office:{keywords:["building","envelope","communication"],char:'\u{1f3e3}',fitzpatrick_scale:!1,category:"travel_and_places"},european_post_office:{keywords:["building","email"],char:'\u{1f3e4}',fitzpatrick_scale:!1,category:"travel_and_places"},hospital:{keywords:["building","health","surgery","doctor"],char:'\u{1f3e5}',fitzpatrick_scale:!1,category:"travel_and_places"},bank:{keywords:["building","money","sales","cash","business","enterprise"],char:'\u{1f3e6}',fitzpatrick_scale:!1,category:"travel_and_places"},hotel:{keywords:["building","accomodation","checkin"],char:'\u{1f3e8}',fitzpatrick_scale:!1,category:"travel_and_places"},convenience_store:{keywords:["building","shopping","groceries"],char:'\u{1f3ea}',fitzpatrick_scale:!1,category:"travel_and_places"},school:{keywords:["building","student","education","learn","teach"],char:'\u{1f3eb}',fitzpatrick_scale:!1,category:"travel_and_places"},love_hotel:{keywords:["like","affection","dating"],char:'\u{1f3e9}',fitzpatrick_scale:!1,category:"travel_and_places"},wedding:{keywords:["love","like","affection","couple","marriage","bride","groom"],char:'\u{1f492}',fitzpatrick_scale:!1,category:"travel_and_places"},classical_building:{keywords:["art","culture","history"],char:'\u{1f3db}',fitzpatrick_scale:!1,category:"travel_and_places"},church:{keywords:["building","religion","christ"],char:'\u26ea',fitzpatrick_scale:!1,category:"travel_and_places"},mosque:{keywords:["islam","worship","minaret"],char:'\u{1f54c}',fitzpatrick_scale:!1,category:"travel_and_places"},synagogue:{keywords:["judaism","worship","temple","jewish"],char:'\u{1f54d}',fitzpatrick_scale:!1,category:"travel_and_places"},kaaba:{keywords:["mecca","mosque","islam"],char:'\u{1f54b}',fitzpatrick_scale:!1,category:"travel_and_places"},shinto_shrine:{keywords:["temple","japan","kyoto"],char:'\u26e9',fitzpatrick_scale:!1,category:"travel_and_places"},watch:{keywords:["time","accessories"],char:'\u231a',fitzpatrick_scale:!1,category:"objects"},iphone:{keywords:["technology","apple","gadgets","dial"],char:'\u{1f4f1}',fitzpatrick_scale:!1,category:"objects"},calling:{keywords:["iphone","incoming"],char:'\u{1f4f2}',fitzpatrick_scale:!1,category:"objects"},computer:{keywords:["technology","laptop","screen","display","monitor"],char:'\u{1f4bb}',fitzpatrick_scale:!1,category:"objects"},keyboard:{keywords:["technology","computer","type","input","text"],char:'\u2328',fitzpatrick_scale:!1,category:"objects"},desktop_computer:{keywords:["technology","computing","screen"],char:'\u{1f5a5}',fitzpatrick_scale:!1,category:"objects"},printer:{keywords:["paper","ink"],char:'\u{1f5a8}',fitzpatrick_scale:!1,category:"objects"},computer_mouse:{keywords:["click"],char:'\u{1f5b1}',fitzpatrick_scale:!1,category:"objects"},trackball:{keywords:["technology","trackpad"],char:'\u{1f5b2}',fitzpatrick_scale:!1,category:"objects"},joystick:{keywords:["game","play"],char:'\u{1f579}',fitzpatrick_scale:!1,category:"objects"},clamp:{keywords:["tool"],char:'\u{1f5dc}',fitzpatrick_scale:!1,category:"objects"},minidisc:{keywords:["technology","record","data","disk","90s"],char:'\u{1f4bd}',fitzpatrick_scale:!1,category:"objects"},floppy_disk:{keywords:["oldschool","technology","save","90s","80s"],char:'\u{1f4be}',fitzpatrick_scale:!1,category:"objects"},cd:{keywords:["technology","dvd","disk","disc","90s"],char:'\u{1f4bf}',fitzpatrick_scale:!1,category:"objects"},dvd:{keywords:["cd","disk","disc"],char:'\u{1f4c0}',fitzpatrick_scale:!1,category:"objects"},vhs:{keywords:["record","video","oldschool","90s","80s"],char:'\u{1f4fc}',fitzpatrick_scale:!1,category:"objects"},camera:{keywords:["gadgets","photography"],char:'\u{1f4f7}',fitzpatrick_scale:!1,category:"objects"},camera_flash:{keywords:["photography","gadgets"],char:'\u{1f4f8}',fitzpatrick_scale:!1,category:"objects"},video_camera:{keywords:["film","record"],char:'\u{1f4f9}',fitzpatrick_scale:!1,category:"objects"},movie_camera:{keywords:["film","record"],char:'\u{1f3a5}',fitzpatrick_scale:!1,category:"objects"},film_projector:{keywords:["video","tape","record","movie"],char:'\u{1f4fd}',fitzpatrick_scale:!1,category:"objects"},film_strip:{keywords:["movie"],char:'\u{1f39e}',fitzpatrick_scale:!1,category:"objects"},telephone_receiver:{keywords:["technology","communication","dial"],char:'\u{1f4de}',fitzpatrick_scale:!1,category:"objects"},phone:{keywords:["technology","communication","dial","telephone"],char:'\u260e\ufe0f',fitzpatrick_scale:!1,category:"objects"},pager:{keywords:["bbcall","oldschool","90s"],char:'\u{1f4df}',fitzpatrick_scale:!1,category:"objects"},fax:{keywords:["communication","technology"],char:'\u{1f4e0}',fitzpatrick_scale:!1,category:"objects"},tv:{keywords:["technology","program","oldschool","show","television"],char:'\u{1f4fa}',fitzpatrick_scale:!1,category:"objects"},radio:{keywords:["communication","music","podcast","program"],char:'\u{1f4fb}',fitzpatrick_scale:!1,category:"objects"},studio_microphone:{keywords:["sing","recording","artist","talkshow"],char:'\u{1f399}',fitzpatrick_scale:!1,category:"objects"},level_slider:{keywords:["scale"],char:'\u{1f39a}',fitzpatrick_scale:!1,category:"objects"},control_knobs:{keywords:["dial"],char:'\u{1f39b}',fitzpatrick_scale:!1,category:"objects"},compass:{keywords:["magnetic","navigation","orienteering"],char:'\u{1f9ed}',fitzpatrick_scale:!1,category:"objects"},stopwatch:{keywords:["time","deadline"],char:'\u23f1',fitzpatrick_scale:!1,category:"objects"},timer_clock:{keywords:["alarm"],char:'\u23f2',fitzpatrick_scale:!1,category:"objects"},alarm_clock:{keywords:["time","wake"],char:'\u23f0',fitzpatrick_scale:!1,category:"objects"},mantelpiece_clock:{keywords:["time"],char:'\u{1f570}',fitzpatrick_scale:!1,category:"objects"},hourglass_flowing_sand:{keywords:["oldschool","time","countdown"],char:'\u23f3',fitzpatrick_scale:!1,category:"objects"},hourglass:{keywords:["time","clock","oldschool","limit","exam","quiz","test"],char:'\u231b',fitzpatrick_scale:!1,category:"objects"},satellite:{keywords:["communication","future","radio","space"],char:'\u{1f4e1}',fitzpatrick_scale:!1,category:"objects"},battery:{keywords:["power","energy","sustain"],char:'\u{1f50b}',fitzpatrick_scale:!1,category:"objects"},electric_plug:{keywords:["charger","power"],char:'\u{1f50c}',fitzpatrick_scale:!1,category:"objects"},bulb:{keywords:["light","electricity","idea"],char:'\u{1f4a1}',fitzpatrick_scale:!1,category:"objects"},flashlight:{keywords:["dark","camping","sight","night"],char:'\u{1f526}',fitzpatrick_scale:!1,category:"objects"},candle:{keywords:["fire","wax"],char:'\u{1f56f}',fitzpatrick_scale:!1,category:"objects"},fire_extinguisher:{keywords:["quench"],char:'\u{1f9ef}',fitzpatrick_scale:!1,category:"objects"},wastebasket:{keywords:["bin","trash","rubbish","garbage","toss"],char:'\u{1f5d1}',fitzpatrick_scale:!1,category:"objects"},oil_drum:{keywords:["barrell"],char:'\u{1f6e2}',fitzpatrick_scale:!1,category:"objects"},money_with_wings:{keywords:["dollar","bills","payment","sale"],char:'\u{1f4b8}',fitzpatrick_scale:!1,category:"objects"},dollar:{keywords:["money","sales","bill","currency"],char:'\u{1f4b5}',fitzpatrick_scale:!1,category:"objects"},yen:{keywords:["money","sales","japanese","dollar","currency"],char:'\u{1f4b4}',fitzpatrick_scale:!1,category:"objects"},euro:{keywords:["money","sales","dollar","currency"],char:'\u{1f4b6}',fitzpatrick_scale:!1,category:"objects"},pound:{keywords:["british","sterling","money","sales","bills","uk","england","currency"],char:'\u{1f4b7}',fitzpatrick_scale:!1,category:"objects"},moneybag:{keywords:["dollar","payment","coins","sale"],char:'\u{1f4b0}',fitzpatrick_scale:!1,category:"objects"},credit_card:{keywords:["money","sales","dollar","bill","payment","shopping"],char:'\u{1f4b3}',fitzpatrick_scale:!1,category:"objects"},gem:{keywords:["blue","ruby","diamond","jewelry"],char:'\u{1f48e}',fitzpatrick_scale:!1,category:"objects"},balance_scale:{keywords:["law","fairness","weight"],char:'\u2696',fitzpatrick_scale:!1,category:"objects"},toolbox:{keywords:["tools","diy","fix","maintainer","mechanic"],char:'\u{1f9f0}',fitzpatrick_scale:!1,category:"objects"},wrench:{keywords:["tools","diy","ikea","fix","maintainer"],char:'\u{1f527}',fitzpatrick_scale:!1,category:"objects"},hammer:{keywords:["tools","build","create"],char:'\u{1f528}',fitzpatrick_scale:!1,category:"objects"},hammer_and_pick:{keywords:["tools","build","create"],char:'\u2692',fitzpatrick_scale:!1,category:"objects"},hammer_and_wrench:{keywords:["tools","build","create"],char:'\u{1f6e0}',fitzpatrick_scale:!1,category:"objects"},pick:{keywords:["tools","dig"],char:'\u26cf',fitzpatrick_scale:!1,category:"objects"},nut_and_bolt:{keywords:["handy","tools","fix"],char:'\u{1f529}',fitzpatrick_scale:!1,category:"objects"},gear:{keywords:["cog"],char:'\u2699',fitzpatrick_scale:!1,category:"objects"},brick:{keywords:["bricks"],char:'\u{1f9f1}',fitzpatrick_scale:!1,category:"objects"},chains:{keywords:["lock","arrest"],char:'\u26d3',fitzpatrick_scale:!1,category:"objects"},magnet:{keywords:["attraction","magnetic"],char:'\u{1f9f2}',fitzpatrick_scale:!1,category:"objects"},gun:{keywords:["violence","weapon","pistol","revolver"],char:'\u{1f52b}',fitzpatrick_scale:!1,category:"objects"},bomb:{keywords:["boom","explode","explosion","terrorism"],char:'\u{1f4a3}',fitzpatrick_scale:!1,category:"objects"},firecracker:{keywords:["dynamite","boom","explode","explosion","explosive"],char:'\u{1f9e8}',fitzpatrick_scale:!1,category:"objects"},hocho:{keywords:["knife","blade","cutlery","kitchen","weapon"],char:'\u{1f52a}',fitzpatrick_scale:!1,category:"objects"},dagger:{keywords:["weapon"],char:'\u{1f5e1}',fitzpatrick_scale:!1,category:"objects"},crossed_swords:{keywords:["weapon"],char:'\u2694',fitzpatrick_scale:!1,category:"objects"},shield:{keywords:["protection","security"],char:'\u{1f6e1}',fitzpatrick_scale:!1,category:"objects"},smoking:{keywords:["kills","tobacco","cigarette","joint","smoke"],char:'\u{1f6ac}',fitzpatrick_scale:!1,category:"objects"},skull_and_crossbones:{keywords:["poison","danger","deadly","scary","death","pirate","evil"],char:'\u2620',fitzpatrick_scale:!1,category:"objects"},coffin:{keywords:["vampire","dead","die","death","rip","graveyard","cemetery","casket","funeral","box"],char:'\u26b0',fitzpatrick_scale:!1,category:"objects"},funeral_urn:{keywords:["dead","die","death","rip","ashes"],char:'\u26b1',fitzpatrick_scale:!1,category:"objects"},amphora:{keywords:["vase","jar"],char:'\u{1f3fa}',fitzpatrick_scale:!1,category:"objects"},crystal_ball:{keywords:["disco","party","magic","circus","fortune_teller"],char:'\u{1f52e}',fitzpatrick_scale:!1,category:"objects"},prayer_beads:{keywords:["dhikr","religious"],char:'\u{1f4ff}',fitzpatrick_scale:!1,category:"objects"},nazar_amulet:{keywords:["bead","charm"],char:'\u{1f9ff}',fitzpatrick_scale:!1,category:"objects"},barber:{keywords:["hair","salon","style"],char:'\u{1f488}',fitzpatrick_scale:!1,category:"objects"},alembic:{keywords:["distilling","science","experiment","chemistry"],char:'\u2697',fitzpatrick_scale:!1,category:"objects"},telescope:{keywords:["stars","space","zoom","science","astronomy"],char:'\u{1f52d}',fitzpatrick_scale:!1,category:"objects"},microscope:{keywords:["laboratory","experiment","zoomin","science","study"],char:'\u{1f52c}',fitzpatrick_scale:!1,category:"objects"},hole:{keywords:["embarrassing"],char:'\u{1f573}',fitzpatrick_scale:!1,category:"objects"},pill:{keywords:["health","medicine","doctor","pharmacy","drug"],char:'\u{1f48a}',fitzpatrick_scale:!1,category:"objects"},syringe:{keywords:["health","hospital","drugs","blood","medicine","needle","doctor","nurse"],char:'\u{1f489}',fitzpatrick_scale:!1,category:"objects"},dna:{keywords:["biologist","genetics","life"],char:'\u{1f9ec}',fitzpatrick_scale:!1,category:"objects"},microbe:{keywords:["amoeba","bacteria","germs"],char:'\u{1f9a0}',fitzpatrick_scale:!1,category:"objects"},petri_dish:{keywords:["bacteria","biology","culture","lab"],char:'\u{1f9eb}',fitzpatrick_scale:!1,category:"objects"},test_tube:{keywords:["chemistry","experiment","lab","science"],char:'\u{1f9ea}',fitzpatrick_scale:!1,category:"objects"},thermometer:{keywords:["weather","temperature","hot","cold"],char:'\u{1f321}',fitzpatrick_scale:!1,category:"objects"},broom:{keywords:["cleaning","sweeping","witch"],char:'\u{1f9f9}',fitzpatrick_scale:!1,category:"objects"},basket:{keywords:["laundry"],char:'\u{1f9fa}',fitzpatrick_scale:!1,category:"objects"},toilet_paper:{keywords:["roll"],char:'\u{1f9fb}',fitzpatrick_scale:!1,category:"objects"},label:{keywords:["sale","tag"],char:'\u{1f3f7}',fitzpatrick_scale:!1,category:"objects"},bookmark:{keywords:["favorite","label","save"],char:'\u{1f516}',fitzpatrick_scale:!1,category:"objects"},toilet:{keywords:["restroom","wc","washroom","bathroom","potty"],char:'\u{1f6bd}',fitzpatrick_scale:!1,category:"objects"},shower:{keywords:["clean","water","bathroom"],char:'\u{1f6bf}',fitzpatrick_scale:!1,category:"objects"},bathtub:{keywords:["clean","shower","bathroom"],char:'\u{1f6c1}',fitzpatrick_scale:!1,category:"objects"},soap:{keywords:["bar","bathing","cleaning","lather"],char:'\u{1f9fc}',fitzpatrick_scale:!1,category:"objects"},sponge:{keywords:["absorbing","cleaning","porous"],char:'\u{1f9fd}',fitzpatrick_scale:!1,category:"objects"},lotion_bottle:{keywords:["moisturizer","sunscreen"],char:'\u{1f9f4}',fitzpatrick_scale:!1,category:"objects"},key:{keywords:["lock","door","password"],char:'\u{1f511}',fitzpatrick_scale:!1,category:"objects"},old_key:{keywords:["lock","door","password"],char:'\u{1f5dd}',fitzpatrick_scale:!1,category:"objects"},couch_and_lamp:{keywords:["read","chill"],char:'\u{1f6cb}',fitzpatrick_scale:!1,category:"objects"},sleeping_bed:{keywords:["bed","rest"],char:'\u{1f6cc}',fitzpatrick_scale:!0,category:"objects"},bed:{keywords:["sleep","rest"],char:'\u{1f6cf}',fitzpatrick_scale:!1,category:"objects"},door:{keywords:["house","entry","exit"],char:'\u{1f6aa}',fitzpatrick_scale:!1,category:"objects"},bellhop_bell:{keywords:["service"],char:'\u{1f6ce}',fitzpatrick_scale:!1,category:"objects"},teddy_bear:{keywords:["plush","stuffed"],char:'\u{1f9f8}',fitzpatrick_scale:!1,category:"objects"},framed_picture:{keywords:["photography"],char:'\u{1f5bc}',fitzpatrick_scale:!1,category:"objects"},world_map:{keywords:["location","direction"],char:'\u{1f5fa}',fitzpatrick_scale:!1,category:"objects"},parasol_on_ground:{keywords:["weather","summer"],char:'\u26f1',fitzpatrick_scale:!1,category:"objects"},moyai:{keywords:["rock","easter island","moai"],char:'\u{1f5ff}',fitzpatrick_scale:!1,category:"objects"},shopping:{keywords:["mall","buy","purchase"],char:'\u{1f6cd}',fitzpatrick_scale:!1,category:"objects"},shopping_cart:{keywords:["trolley"],char:'\u{1f6d2}',fitzpatrick_scale:!1,category:"objects"},balloon:{keywords:["party","celebration","birthday","circus"],char:'\u{1f388}',fitzpatrick_scale:!1,category:"objects"},flags:{keywords:["fish","japanese","koinobori","carp","banner"],char:'\u{1f38f}',fitzpatrick_scale:!1,category:"objects"},ribbon:{keywords:["decoration","pink","girl","bowtie"],char:'\u{1f380}',fitzpatrick_scale:!1,category:"objects"},gift:{keywords:["present","birthday","christmas","xmas"],char:'\u{1f381}',fitzpatrick_scale:!1,category:"objects"},confetti_ball:{keywords:["festival","party","birthday","circus"],char:'\u{1f38a}',fitzpatrick_scale:!1,category:"objects"},tada:{keywords:["party","congratulations","birthday","magic","circus","celebration"],char:'\u{1f389}',fitzpatrick_scale:!1,category:"objects"},dolls:{keywords:["japanese","toy","kimono"],char:'\u{1f38e}',fitzpatrick_scale:!1,category:"objects"},wind_chime:{keywords:["nature","ding","spring","bell"],char:'\u{1f390}',fitzpatrick_scale:!1,category:"objects"},crossed_flags:{keywords:["japanese","nation","country","border"],char:'\u{1f38c}',fitzpatrick_scale:!1,category:"objects"},izakaya_lantern:{keywords:["light","paper","halloween","spooky"],char:'\u{1f3ee}',fitzpatrick_scale:!1,category:"objects"},red_envelope:{keywords:["gift"],char:'\u{1f9e7}',fitzpatrick_scale:!1,category:"objects"},email:{keywords:["letter","postal","inbox","communication"],char:'\u2709\ufe0f',fitzpatrick_scale:!1,category:"objects"},envelope_with_arrow:{keywords:["email","communication"],char:'\u{1f4e9}',fitzpatrick_scale:!1,category:"objects"},incoming_envelope:{keywords:["email","inbox"],char:'\u{1f4e8}',fitzpatrick_scale:!1,category:"objects"},"e-mail":{keywords:["communication","inbox"],char:'\u{1f4e7}',fitzpatrick_scale:!1,category:"objects"},love_letter:{keywords:["email","like","affection","envelope","valentines"],char:'\u{1f48c}',fitzpatrick_scale:!1,category:"objects"},postbox:{keywords:["email","letter","envelope"],char:'\u{1f4ee}',fitzpatrick_scale:!1,category:"objects"},mailbox_closed:{keywords:["email","communication","inbox"],char:'\u{1f4ea}',fitzpatrick_scale:!1,category:"objects"},mailbox:{keywords:["email","inbox","communication"],char:'\u{1f4eb}',fitzpatrick_scale:!1,category:"objects"},mailbox_with_mail:{keywords:["email","inbox","communication"],char:'\u{1f4ec}',fitzpatrick_scale:!1,category:"objects"},mailbox_with_no_mail:{keywords:["email","inbox"],char:'\u{1f4ed}',fitzpatrick_scale:!1,category:"objects"},package:{keywords:["mail","gift","cardboard","box","moving"],char:'\u{1f4e6}',fitzpatrick_scale:!1,category:"objects"},postal_horn:{keywords:["instrument","music"],char:'\u{1f4ef}',fitzpatrick_scale:!1,category:"objects"},inbox_tray:{keywords:["email","documents"],char:'\u{1f4e5}',fitzpatrick_scale:!1,category:"objects"},outbox_tray:{keywords:["inbox","email"],char:'\u{1f4e4}',fitzpatrick_scale:!1,category:"objects"},scroll:{keywords:["documents","ancient","history","paper"],char:'\u{1f4dc}',fitzpatrick_scale:!1,category:"objects"},page_with_curl:{keywords:["documents","office","paper"],char:'\u{1f4c3}',fitzpatrick_scale:!1,category:"objects"},bookmark_tabs:{keywords:["favorite","save","order","tidy"],char:'\u{1f4d1}',fitzpatrick_scale:!1,category:"objects"},receipt:{keywords:["accounting","expenses"],char:'\u{1f9fe}',fitzpatrick_scale:!1,category:"objects"},bar_chart:{keywords:["graph","presentation","stats"],char:'\u{1f4ca}',fitzpatrick_scale:!1,category:"objects"},chart_with_upwards_trend:{keywords:["graph","presentation","stats","recovery","business","economics","money","sales","good","success"],char:'\u{1f4c8}',fitzpatrick_scale:!1,category:"objects"},chart_with_downwards_trend:{keywords:["graph","presentation","stats","recession","business","economics","money","sales","bad","failure"],char:'\u{1f4c9}',fitzpatrick_scale:!1,category:"objects"},page_facing_up:{keywords:["documents","office","paper","information"],char:'\u{1f4c4}',fitzpatrick_scale:!1,category:"objects"},date:{keywords:["calendar","schedule"],char:'\u{1f4c5}',fitzpatrick_scale:!1,category:"objects"},calendar:{keywords:["schedule","date","planning"],char:'\u{1f4c6}',fitzpatrick_scale:!1,category:"objects"},spiral_calendar:{keywords:["date","schedule","planning"],char:'\u{1f5d3}',fitzpatrick_scale:!1,category:"objects"},card_index:{keywords:["business","stationery"],char:'\u{1f4c7}',fitzpatrick_scale:!1,category:"objects"},card_file_box:{keywords:["business","stationery"],char:'\u{1f5c3}',fitzpatrick_scale:!1,category:"objects"},ballot_box:{keywords:["election","vote"],char:'\u{1f5f3}',fitzpatrick_scale:!1,category:"objects"},file_cabinet:{keywords:["filing","organizing"],char:'\u{1f5c4}',fitzpatrick_scale:!1,category:"objects"},clipboard:{keywords:["stationery","documents"],char:'\u{1f4cb}',fitzpatrick_scale:!1,category:"objects"},spiral_notepad:{keywords:["memo","stationery"],char:'\u{1f5d2}',fitzpatrick_scale:!1,category:"objects"},file_folder:{keywords:["documents","business","office"],char:'\u{1f4c1}',fitzpatrick_scale:!1,category:"objects"},open_file_folder:{keywords:["documents","load"],char:'\u{1f4c2}',fitzpatrick_scale:!1,category:"objects"},card_index_dividers:{keywords:["organizing","business","stationery"],char:'\u{1f5c2}',fitzpatrick_scale:!1,category:"objects"},newspaper_roll:{keywords:["press","headline"],char:'\u{1f5de}',fitzpatrick_scale:!1,category:"objects"},newspaper:{keywords:["press","headline"],char:'\u{1f4f0}',fitzpatrick_scale:!1,category:"objects"},notebook:{keywords:["stationery","record","notes","paper","study"],char:'\u{1f4d3}',fitzpatrick_scale:!1,category:"objects"},closed_book:{keywords:["read","library","knowledge","textbook","learn"],char:'\u{1f4d5}',fitzpatrick_scale:!1,category:"objects"},green_book:{keywords:["read","library","knowledge","study"],char:'\u{1f4d7}',fitzpatrick_scale:!1,category:"objects"},blue_book:{keywords:["read","library","knowledge","learn","study"],char:'\u{1f4d8}',fitzpatrick_scale:!1,category:"objects"},orange_book:{keywords:["read","library","knowledge","textbook","study"],char:'\u{1f4d9}',fitzpatrick_scale:!1,category:"objects"},notebook_with_decorative_cover:{keywords:["classroom","notes","record","paper","study"],char:'\u{1f4d4}',fitzpatrick_scale:!1,category:"objects"},ledger:{keywords:["notes","paper"],char:'\u{1f4d2}',fitzpatrick_scale:!1,category:"objects"},books:{keywords:["literature","library","study"],char:'\u{1f4da}',fitzpatrick_scale:!1,category:"objects"},open_book:{keywords:["book","read","library","knowledge","literature","learn","study"],char:'\u{1f4d6}',fitzpatrick_scale:!1,category:"objects"},safety_pin:{keywords:["diaper"],char:'\u{1f9f7}',fitzpatrick_scale:!1,category:"objects"},link:{keywords:["rings","url"],char:'\u{1f517}',fitzpatrick_scale:!1,category:"objects"},paperclip:{keywords:["documents","stationery"],char:'\u{1f4ce}',fitzpatrick_scale:!1,category:"objects"},paperclips:{keywords:["documents","stationery"],char:'\u{1f587}',fitzpatrick_scale:!1,category:"objects"},scissors:{keywords:["stationery","cut"],char:'\u2702\ufe0f',fitzpatrick_scale:!1,category:"objects"},triangular_ruler:{keywords:["stationery","math","architect","sketch"],char:'\u{1f4d0}',fitzpatrick_scale:!1,category:"objects"},straight_ruler:{keywords:["stationery","calculate","length","math","school","drawing","architect","sketch"],char:'\u{1f4cf}',fitzpatrick_scale:!1,category:"objects"},abacus:{keywords:["calculation"],char:'\u{1f9ee}',fitzpatrick_scale:!1,category:"objects"},pushpin:{keywords:["stationery","mark","here"],char:'\u{1f4cc}',fitzpatrick_scale:!1,category:"objects"},round_pushpin:{keywords:["stationery","location","map","here"],char:'\u{1f4cd}',fitzpatrick_scale:!1,category:"objects"},triangular_flag_on_post:{keywords:["mark","milestone","place"],char:'\u{1f6a9}',fitzpatrick_scale:!1,category:"objects"},white_flag:{keywords:["losing","loser","lost","surrender","give up","fail"],char:'\u{1f3f3}',fitzpatrick_scale:!1,category:"objects"},black_flag:{keywords:["pirate"],char:'\u{1f3f4}',fitzpatrick_scale:!1,category:"objects"},rainbow_flag:{keywords:["flag","rainbow","pride","gay","lgbt","glbt","queer","homosexual","lesbian","bisexual","transgender"],char:'\u{1f3f3}\ufe0f\u200d\u{1f308}',fitzpatrick_scale:!1,category:"objects"},closed_lock_with_key:{keywords:["security","privacy"],char:'\u{1f510}',fitzpatrick_scale:!1,category:"objects"},lock:{keywords:["security","password","padlock"],char:'\u{1f512}',fitzpatrick_scale:!1,category:"objects"},unlock:{keywords:["privacy","security"],char:'\u{1f513}',fitzpatrick_scale:!1,category:"objects"},lock_with_ink_pen:{keywords:["security","secret"],char:'\u{1f50f}',fitzpatrick_scale:!1,category:"objects"},pen:{keywords:["stationery","writing","write"],char:'\u{1f58a}',fitzpatrick_scale:!1,category:"objects"},fountain_pen:{keywords:["stationery","writing","write"],char:'\u{1f58b}',fitzpatrick_scale:!1,category:"objects"},black_nib:{keywords:["pen","stationery","writing","write"],char:'\u2712\ufe0f',fitzpatrick_scale:!1,category:"objects"},memo:{keywords:["write","documents","stationery","pencil","paper","writing","legal","exam","quiz","test","study","compose"],char:'\u{1f4dd}',fitzpatrick_scale:!1,category:"objects"},pencil2:{keywords:["stationery","write","paper","writing","school","study"],char:'\u270f\ufe0f',fitzpatrick_scale:!1,category:"objects"},crayon:{keywords:["drawing","creativity"],char:'\u{1f58d}',fitzpatrick_scale:!1,category:"objects"},paintbrush:{keywords:["drawing","creativity","art"],char:'\u{1f58c}',fitzpatrick_scale:!1,category:"objects"},mag:{keywords:["search","zoom","find","detective"],char:'\u{1f50d}',fitzpatrick_scale:!1,category:"objects"},mag_right:{keywords:["search","zoom","find","detective"],char:'\u{1f50e}',fitzpatrick_scale:!1,category:"objects"},heart:{keywords:["love","like","valentines"],char:'\u2764\ufe0f',fitzpatrick_scale:!1,category:"symbols"},orange_heart:{keywords:["love","like","affection","valentines"],char:'\u{1f9e1}',fitzpatrick_scale:!1,category:"symbols"},yellow_heart:{keywords:["love","like","affection","valentines"],char:'\u{1f49b}',fitzpatrick_scale:!1,category:"symbols"},green_heart:{keywords:["love","like","affection","valentines"],char:'\u{1f49a}',fitzpatrick_scale:!1,category:"symbols"},blue_heart:{keywords:["love","like","affection","valentines"],char:'\u{1f499}',fitzpatrick_scale:!1,category:"symbols"},purple_heart:{keywords:["love","like","affection","valentines"],char:'\u{1f49c}',fitzpatrick_scale:!1,category:"symbols"},black_heart:{keywords:["evil"],char:'\u{1f5a4}',fitzpatrick_scale:!1,category:"symbols"},broken_heart:{keywords:["sad","sorry","break","heart","heartbreak"],char:'\u{1f494}',fitzpatrick_scale:!1,category:"symbols"},heavy_heart_exclamation:{keywords:["decoration","love"],char:'\u2763',fitzpatrick_scale:!1,category:"symbols"},two_hearts:{keywords:["love","like","affection","valentines","heart"],char:'\u{1f495}',fitzpatrick_scale:!1,category:"symbols"},revolving_hearts:{keywords:["love","like","affection","valentines"],char:'\u{1f49e}',fitzpatrick_scale:!1,category:"symbols"},heartbeat:{keywords:["love","like","affection","valentines","pink","heart"],char:'\u{1f493}',fitzpatrick_scale:!1,category:"symbols"},heartpulse:{keywords:["like","love","affection","valentines","pink"],char:'\u{1f497}',fitzpatrick_scale:!1,category:"symbols"},sparkling_heart:{keywords:["love","like","affection","valentines"],char:'\u{1f496}',fitzpatrick_scale:!1,category:"symbols"},cupid:{keywords:["love","like","heart","affection","valentines"],char:'\u{1f498}',fitzpatrick_scale:!1,category:"symbols"},gift_heart:{keywords:["love","valentines"],char:'\u{1f49d}',fitzpatrick_scale:!1,category:"symbols"},heart_decoration:{keywords:["purple-square","love","like"],char:'\u{1f49f}',fitzpatrick_scale:!1,category:"symbols"},peace_symbol:{keywords:["hippie"],char:'\u262e',fitzpatrick_scale:!1,category:"symbols"},latin_cross:{keywords:["christianity"],char:'\u271d',fitzpatrick_scale:!1,category:"symbols"},star_and_crescent:{keywords:["islam"],char:'\u262a',fitzpatrick_scale:!1,category:"symbols"},om:{keywords:["hinduism","buddhism","sikhism","jainism"],char:'\u{1f549}',fitzpatrick_scale:!1,category:"symbols"},wheel_of_dharma:{keywords:["hinduism","buddhism","sikhism","jainism"],char:'\u2638',fitzpatrick_scale:!1,category:"symbols"},star_of_david:{keywords:["judaism"],char:'\u2721',fitzpatrick_scale:!1,category:"symbols"},six_pointed_star:{keywords:["purple-square","religion","jewish","hexagram"],char:'\u{1f52f}',fitzpatrick_scale:!1,category:"symbols"},menorah:{keywords:["hanukkah","candles","jewish"],char:'\u{1f54e}',fitzpatrick_scale:!1,category:"symbols"},yin_yang:{keywords:["balance"],char:'\u262f',fitzpatrick_scale:!1,category:"symbols"},orthodox_cross:{keywords:["suppedaneum","religion"],char:'\u2626',fitzpatrick_scale:!1,category:"symbols"},place_of_worship:{keywords:["religion","church","temple","prayer"],char:'\u{1f6d0}',fitzpatrick_scale:!1,category:"symbols"},ophiuchus:{keywords:["sign","purple-square","constellation","astrology"],char:'\u26ce',fitzpatrick_scale:!1,category:"symbols"},aries:{keywords:["sign","purple-square","zodiac","astrology"],char:'\u2648',fitzpatrick_scale:!1,category:"symbols"},taurus:{keywords:["purple-square","sign","zodiac","astrology"],char:'\u2649',fitzpatrick_scale:!1,category:"symbols"},gemini:{keywords:["sign","zodiac","purple-square","astrology"],char:'\u264a',fitzpatrick_scale:!1,category:"symbols"},cancer:{keywords:["sign","zodiac","purple-square","astrology"],char:'\u264b',fitzpatrick_scale:!1,category:"symbols"},leo:{keywords:["sign","purple-square","zodiac","astrology"],char:'\u264c',fitzpatrick_scale:!1,category:"symbols"},virgo:{keywords:["sign","zodiac","purple-square","astrology"],char:'\u264d',fitzpatrick_scale:!1,category:"symbols"},libra:{keywords:["sign","purple-square","zodiac","astrology"],char:'\u264e',fitzpatrick_scale:!1,category:"symbols"},scorpius:{keywords:["sign","zodiac","purple-square","astrology","scorpio"],char:'\u264f',fitzpatrick_scale:!1,category:"symbols"},sagittarius:{keywords:["sign","zodiac","purple-square","astrology"],char:'\u2650',fitzpatrick_scale:!1,category:"symbols"},capricorn:{keywords:["sign","zodiac","purple-square","astrology"],char:'\u2651',fitzpatrick_scale:!1,category:"symbols"},aquarius:{keywords:["sign","purple-square","zodiac","astrology"],char:'\u2652',fitzpatrick_scale:!1,category:"symbols"},pisces:{keywords:["purple-square","sign","zodiac","astrology"],char:'\u2653',fitzpatrick_scale:!1,category:"symbols"},id:{keywords:["purple-square","words"],char:'\u{1f194}',fitzpatrick_scale:!1,category:"symbols"},atom_symbol:{keywords:["science","physics","chemistry"],char:'\u269b',fitzpatrick_scale:!1,category:"symbols"},u7a7a:{keywords:["kanji","japanese","chinese","empty","sky","blue-square"],char:'\u{1f233}',fitzpatrick_scale:!1,category:"symbols"},u5272:{keywords:["cut","divide","chinese","kanji","pink-square"],char:'\u{1f239}',fitzpatrick_scale:!1,category:"symbols"},radioactive:{keywords:["nuclear","danger"],char:'\u2622',fitzpatrick_scale:!1,category:"symbols"},biohazard:{keywords:["danger"],char:'\u2623',fitzpatrick_scale:!1,category:"symbols"},mobile_phone_off:{keywords:["mute","orange-square","silence","quiet"],char:'\u{1f4f4}',fitzpatrick_scale:!1,category:"symbols"},vibration_mode:{keywords:["orange-square","phone"],char:'\u{1f4f3}',fitzpatrick_scale:!1,category:"symbols"},u6709:{keywords:["orange-square","chinese","have","kanji"],char:'\u{1f236}',fitzpatrick_scale:!1,category:"symbols"},u7121:{keywords:["nothing","chinese","kanji","japanese","orange-square"],char:'\u{1f21a}',fitzpatrick_scale:!1,category:"symbols"},u7533:{keywords:["chinese","japanese","kanji","orange-square"],char:'\u{1f238}',fitzpatrick_scale:!1,category:"symbols"},u55b6:{keywords:["japanese","opening hours","orange-square"],char:'\u{1f23a}',fitzpatrick_scale:!1,category:"symbols"},u6708:{keywords:["chinese","month","moon","japanese","orange-square","kanji"],char:'\u{1f237}\ufe0f',fitzpatrick_scale:!1,category:"symbols"},eight_pointed_black_star:{keywords:["orange-square","shape","polygon"],char:'\u2734\ufe0f',fitzpatrick_scale:!1,category:"symbols"},vs:{keywords:["words","orange-square"],char:'\u{1f19a}',fitzpatrick_scale:!1,category:"symbols"},accept:{keywords:["ok","good","chinese","kanji","agree","yes","orange-circle"],char:'\u{1f251}',fitzpatrick_scale:!1,category:"symbols"},white_flower:{keywords:["japanese","spring"],char:'\u{1f4ae}',fitzpatrick_scale:!1,category:"symbols"},ideograph_advantage:{keywords:["chinese","kanji","obtain","get","circle"],char:'\u{1f250}',fitzpatrick_scale:!1,category:"symbols"},secret:{keywords:["privacy","chinese","sshh","kanji","red-circle"],char:'\u3299\ufe0f',fitzpatrick_scale:!1,category:"symbols"},congratulations:{keywords:["chinese","kanji","japanese","red-circle"],char:'\u3297\ufe0f',fitzpatrick_scale:!1,category:"symbols"},u5408:{keywords:["japanese","chinese","join","kanji","red-square"],char:'\u{1f234}',fitzpatrick_scale:!1,category:"symbols"},u6e80:{keywords:["full","chinese","japanese","red-square","kanji"],char:'\u{1f235}',fitzpatrick_scale:!1,category:"symbols"},u7981:{keywords:["kanji","japanese","chinese","forbidden","limit","restricted","red-square"],char:'\u{1f232}',fitzpatrick_scale:!1,category:"symbols"},a:{keywords:["red-square","alphabet","letter"],char:'\u{1f170}\ufe0f',fitzpatrick_scale:!1,category:"symbols"},b:{keywords:["red-square","alphabet","letter"],char:'\u{1f171}\ufe0f',fitzpatrick_scale:!1,category:"symbols"},ab:{keywords:["red-square","alphabet"],char:'\u{1f18e}',fitzpatrick_scale:!1,category:"symbols"},cl:{keywords:["alphabet","words","red-square"],char:'\u{1f191}',fitzpatrick_scale:!1,category:"symbols"},o2:{keywords:["alphabet","red-square","letter"],char:'\u{1f17e}\ufe0f',fitzpatrick_scale:!1,category:"symbols"},sos:{keywords:["help","red-square","words","emergency","911"],char:'\u{1f198}',fitzpatrick_scale:!1,category:"symbols"},no_entry:{keywords:["limit","security","privacy","bad","denied","stop","circle"],char:'\u26d4',fitzpatrick_scale:!1,category:"symbols"},name_badge:{keywords:["fire","forbid"],char:'\u{1f4db}',fitzpatrick_scale:!1,category:"symbols"},no_entry_sign:{keywords:["forbid","stop","limit","denied","disallow","circle"],char:'\u{1f6ab}',fitzpatrick_scale:!1,category:"symbols"},x:{keywords:["no","delete","remove","cancel","red"],char:'\u274c',fitzpatrick_scale:!1,category:"symbols"},o:{keywords:["circle","round"],char:'\u2b55',fitzpatrick_scale:!1,category:"symbols"},stop_sign:{keywords:["stop"],char:'\u{1f6d1}',fitzpatrick_scale:!1,category:"symbols"},anger:{keywords:["angry","mad"],char:'\u{1f4a2}',fitzpatrick_scale:!1,category:"symbols"},hotsprings:{keywords:["bath","warm","relax"],char:'\u2668\ufe0f',fitzpatrick_scale:!1,category:"symbols"},no_pedestrians:{keywords:["rules","crossing","walking","circle"],char:'\u{1f6b7}',fitzpatrick_scale:!1,category:"symbols"},do_not_litter:{keywords:["trash","bin","garbage","circle"],char:'\u{1f6af}',fitzpatrick_scale:!1,category:"symbols"},no_bicycles:{keywords:["cyclist","prohibited","circle"],char:'\u{1f6b3}',fitzpatrick_scale:!1,category:"symbols"},"non-potable_water":{keywords:["drink","faucet","tap","circle"],char:'\u{1f6b1}',fitzpatrick_scale:!1,category:"symbols"},underage:{keywords:["18","drink","pub","night","minor","circle"],char:'\u{1f51e}',fitzpatrick_scale:!1,category:"symbols"},no_mobile_phones:{keywords:["iphone","mute","circle"],char:'\u{1f4f5}',fitzpatrick_scale:!1,category:"symbols"},exclamation:{keywords:["heavy_exclamation_mark","danger","surprise","punctuation","wow","warning"],char:'\u2757',fitzpatrick_scale:!1,category:"symbols"},grey_exclamation:{keywords:["surprise","punctuation","gray","wow","warning"],char:'\u2755',fitzpatrick_scale:!1,category:"symbols"},question:{keywords:["doubt","confused"],char:'\u2753',fitzpatrick_scale:!1,category:"symbols"},grey_question:{keywords:["doubts","gray","huh","confused"],char:'\u2754',fitzpatrick_scale:!1,category:"symbols"},bangbang:{keywords:["exclamation","surprise"],char:'\u203c\ufe0f',fitzpatrick_scale:!1,category:"symbols"},interrobang:{keywords:["wat","punctuation","surprise"],char:'\u2049\ufe0f',fitzpatrick_scale:!1,category:"symbols"},low_brightness:{keywords:["sun","afternoon","warm","summer"],char:'\u{1f505}',fitzpatrick_scale:!1,category:"symbols"},high_brightness:{keywords:["sun","light"],char:'\u{1f506}',fitzpatrick_scale:!1,category:"symbols"},trident:{keywords:["weapon","spear"],char:'\u{1f531}',fitzpatrick_scale:!1,category:"symbols"},fleur_de_lis:{keywords:["decorative","scout"],char:'\u269c',fitzpatrick_scale:!1,category:"symbols"},part_alternation_mark:{keywords:["graph","presentation","stats","business","economics","bad"],char:'\u303d\ufe0f',fitzpatrick_scale:!1,category:"symbols"},warning:{keywords:["exclamation","wip","alert","error","problem","issue"],char:'\u26a0\ufe0f',fitzpatrick_scale:!1,category:"symbols"},children_crossing:{keywords:["school","warning","danger","sign","driving","yellow-diamond"],char:'\u{1f6b8}',fitzpatrick_scale:!1,category:"symbols"},beginner:{keywords:["badge","shield"],char:'\u{1f530}',fitzpatrick_scale:!1,category:"symbols"},recycle:{keywords:["arrow","environment","garbage","trash"],char:'\u267b\ufe0f',fitzpatrick_scale:!1,category:"symbols"},u6307:{keywords:["chinese","point","green-square","kanji"],char:'\u{1f22f}',fitzpatrick_scale:!1,category:"symbols"},chart:{keywords:["green-square","graph","presentation","stats"],char:'\u{1f4b9}',fitzpatrick_scale:!1,category:"symbols"},sparkle:{keywords:["stars","green-square","awesome","good","fireworks"],char:'\u2747\ufe0f',fitzpatrick_scale:!1,category:"symbols"},eight_spoked_asterisk:{keywords:["star","sparkle","green-square"],char:'\u2733\ufe0f',fitzpatrick_scale:!1,category:"symbols"},negative_squared_cross_mark:{keywords:["x","green-square","no","deny"],char:'\u274e',fitzpatrick_scale:!1,category:"symbols"},white_check_mark:{keywords:["green-square","ok","agree","vote","election","answer","tick"],char:'\u2705',fitzpatrick_scale:!1,category:"symbols"},diamond_shape_with_a_dot_inside:{keywords:["jewel","blue","gem","crystal","fancy"],char:'\u{1f4a0}',fitzpatrick_scale:!1,category:"symbols"},cyclone:{keywords:["weather","swirl","blue","cloud","vortex","spiral","whirlpool","spin","tornado","hurricane","typhoon"],char:'\u{1f300}',fitzpatrick_scale:!1,category:"symbols"},loop:{keywords:["tape","cassette"],char:'\u27bf',fitzpatrick_scale:!1,category:"symbols"},globe_with_meridians:{keywords:["earth","international","world","internet","interweb","i18n"],char:'\u{1f310}',fitzpatrick_scale:!1,category:"symbols"},m:{keywords:["alphabet","blue-circle","letter"],char:'\u24c2\ufe0f',fitzpatrick_scale:!1,category:"symbols"},atm:{keywords:["money","sales","cash","blue-square","payment","bank"],char:'\u{1f3e7}',fitzpatrick_scale:!1,category:"symbols"},sa:{keywords:["japanese","blue-square","katakana"],char:'\u{1f202}\ufe0f',fitzpatrick_scale:!1,category:"symbols"},passport_control:{keywords:["custom","blue-square"],char:'\u{1f6c2}',fitzpatrick_scale:!1,category:"symbols"},customs:{keywords:["passport","border","blue-square"],char:'\u{1f6c3}',fitzpatrick_scale:!1,category:"symbols"},baggage_claim:{keywords:["blue-square","airport","transport"],char:'\u{1f6c4}',fitzpatrick_scale:!1,category:"symbols"},left_luggage:{keywords:["blue-square","travel"],char:'\u{1f6c5}',fitzpatrick_scale:!1,category:"symbols"},wheelchair:{keywords:["blue-square","disabled","a11y","accessibility"],char:'\u267f',fitzpatrick_scale:!1,category:"symbols"},no_smoking:{keywords:["cigarette","blue-square","smell","smoke"],char:'\u{1f6ad}',fitzpatrick_scale:!1,category:"symbols"},wc:{keywords:["toilet","restroom","blue-square"],char:'\u{1f6be}',fitzpatrick_scale:!1,category:"symbols"},parking:{keywords:["cars","blue-square","alphabet","letter"],char:'\u{1f17f}\ufe0f',fitzpatrick_scale:!1,category:"symbols"},potable_water:{keywords:["blue-square","liquid","restroom","cleaning","faucet"],char:'\u{1f6b0}',fitzpatrick_scale:!1,category:"symbols"},mens:{keywords:["toilet","restroom","wc","blue-square","gender","male"],char:'\u{1f6b9}',fitzpatrick_scale:!1,category:"symbols"},womens:{keywords:["purple-square","woman","female","toilet","loo","restroom","gender"],char:'\u{1f6ba}',fitzpatrick_scale:!1,category:"symbols"},baby_symbol:{keywords:["orange-square","child"],char:'\u{1f6bc}',fitzpatrick_scale:!1,category:"symbols"},restroom:{keywords:["blue-square","toilet","refresh","wc","gender"],char:'\u{1f6bb}',fitzpatrick_scale:!1,category:"symbols"},put_litter_in_its_place:{keywords:["blue-square","sign","human","info"],char:'\u{1f6ae}',fitzpatrick_scale:!1,category:"symbols"},cinema:{keywords:["blue-square","record","film","movie","curtain","stage","theater"],char:'\u{1f3a6}',fitzpatrick_scale:!1,category:"symbols"},signal_strength:{keywords:["blue-square","reception","phone","internet","connection","wifi","bluetooth","bars"],char:'\u{1f4f6}',fitzpatrick_scale:!1,category:"symbols"},koko:{keywords:["blue-square","here","katakana","japanese","destination"],char:'\u{1f201}',fitzpatrick_scale:!1,category:"symbols"},ng:{keywords:["blue-square","words","shape","icon"],char:'\u{1f196}',fitzpatrick_scale:!1,category:"symbols"},ok:{keywords:["good","agree","yes","blue-square"],char:'\u{1f197}',fitzpatrick_scale:!1,category:"symbols"},up:{keywords:["blue-square","above","high"],char:'\u{1f199}',fitzpatrick_scale:!1,category:"symbols"},cool:{keywords:["words","blue-square"],char:'\u{1f192}',fitzpatrick_scale:!1,category:"symbols"},new:{keywords:["blue-square","words","start"],char:'\u{1f195}',fitzpatrick_scale:!1,category:"symbols"},free:{keywords:["blue-square","words"],char:'\u{1f193}',fitzpatrick_scale:!1,category:"symbols"},zero:{keywords:["0","numbers","blue-square","null"],char:'0\ufe0f\u20e3',fitzpatrick_scale:!1,category:"symbols"},one:{keywords:["blue-square","numbers","1"],char:'1\ufe0f\u20e3',fitzpatrick_scale:!1,category:"symbols"},two:{keywords:["numbers","2","prime","blue-square"],char:'2\ufe0f\u20e3',fitzpatrick_scale:!1,category:"symbols"},three:{keywords:["3","numbers","prime","blue-square"],char:'3\ufe0f\u20e3',fitzpatrick_scale:!1,category:"symbols"},four:{keywords:["4","numbers","blue-square"],char:'4\ufe0f\u20e3',fitzpatrick_scale:!1,category:"symbols"},five:{keywords:["5","numbers","blue-square","prime"],char:'5\ufe0f\u20e3',fitzpatrick_scale:!1,category:"symbols"},six:{keywords:["6","numbers","blue-square"],char:'6\ufe0f\u20e3',fitzpatrick_scale:!1,category:"symbols"},seven:{keywords:["7","numbers","blue-square","prime"],char:'7\ufe0f\u20e3',fitzpatrick_scale:!1,category:"symbols"},eight:{keywords:["8","blue-square","numbers"],char:'8\ufe0f\u20e3',fitzpatrick_scale:!1,category:"symbols"},nine:{keywords:["blue-square","numbers","9"],char:'9\ufe0f\u20e3',fitzpatrick_scale:!1,category:"symbols"},keycap_ten:{keywords:["numbers","10","blue-square"],char:'\u{1f51f}',fitzpatrick_scale:!1,category:"symbols"},asterisk:{keywords:["star","keycap"],char:'*\u20e3',fitzpatrick_scale:!1,category:"symbols"},eject_button:{keywords:["blue-square"],char:'\u23cf\ufe0f',fitzpatrick_scale:!1,category:"symbols"},arrow_forward:{keywords:["blue-square","right","direction","play"],char:'\u25b6\ufe0f',fitzpatrick_scale:!1,category:"symbols"},pause_button:{keywords:["pause","blue-square"],char:'\u23f8',fitzpatrick_scale:!1,category:"symbols"},next_track_button:{keywords:["forward","next","blue-square"],char:'\u23ed',fitzpatrick_scale:!1,category:"symbols"},stop_button:{keywords:["blue-square"],char:'\u23f9',fitzpatrick_scale:!1,category:"symbols"},record_button:{keywords:["blue-square"],char:'\u23fa',fitzpatrick_scale:!1,category:"symbols"},play_or_pause_button:{keywords:["blue-square","play","pause"],char:'\u23ef',fitzpatrick_scale:!1,category:"symbols"},previous_track_button:{keywords:["backward"],char:'\u23ee',fitzpatrick_scale:!1,category:"symbols"},fast_forward:{keywords:["blue-square","play","speed","continue"],char:'\u23e9',fitzpatrick_scale:!1,category:"symbols"},rewind:{keywords:["play","blue-square"],char:'\u23ea',fitzpatrick_scale:!1,category:"symbols"},twisted_rightwards_arrows:{keywords:["blue-square","shuffle","music","random"],char:'\u{1f500}',fitzpatrick_scale:!1,category:"symbols"},repeat:{keywords:["loop","record"],char:'\u{1f501}',fitzpatrick_scale:!1,category:"symbols"},repeat_one:{keywords:["blue-square","loop"],char:'\u{1f502}',fitzpatrick_scale:!1,category:"symbols"},arrow_backward:{keywords:["blue-square","left","direction"],char:'\u25c0\ufe0f',fitzpatrick_scale:!1,category:"symbols"},arrow_up_small:{keywords:["blue-square","triangle","direction","point","forward","top"],char:'\u{1f53c}',fitzpatrick_scale:!1,category:"symbols"},arrow_down_small:{keywords:["blue-square","direction","bottom"],char:'\u{1f53d}',fitzpatrick_scale:!1,category:"symbols"},arrow_double_up:{keywords:["blue-square","direction","top"],char:'\u23eb',fitzpatrick_scale:!1,category:"symbols"},arrow_double_down:{keywords:["blue-square","direction","bottom"],char:'\u23ec',fitzpatrick_scale:!1,category:"symbols"},arrow_right:{keywords:["blue-square","next"],char:'\u27a1\ufe0f',fitzpatrick_scale:!1,category:"symbols"},arrow_left:{keywords:["blue-square","previous","back"],char:'\u2b05\ufe0f',fitzpatrick_scale:!1,category:"symbols"},arrow_up:{keywords:["blue-square","continue","top","direction"],char:'\u2b06\ufe0f',fitzpatrick_scale:!1,category:"symbols"},arrow_down:{keywords:["blue-square","direction","bottom"],char:'\u2b07\ufe0f',fitzpatrick_scale:!1,category:"symbols"},arrow_upper_right:{keywords:["blue-square","point","direction","diagonal","northeast"],char:'\u2197\ufe0f',fitzpatrick_scale:!1,category:"symbols"},arrow_lower_right:{keywords:["blue-square","direction","diagonal","southeast"],char:'\u2198\ufe0f',fitzpatrick_scale:!1,category:"symbols"},arrow_lower_left:{keywords:["blue-square","direction","diagonal","southwest"],char:'\u2199\ufe0f',fitzpatrick_scale:!1,category:"symbols"},arrow_upper_left:{keywords:["blue-square","point","direction","diagonal","northwest"],char:'\u2196\ufe0f',fitzpatrick_scale:!1,category:"symbols"},arrow_up_down:{keywords:["blue-square","direction","way","vertical"],char:'\u2195\ufe0f',fitzpatrick_scale:!1,category:"symbols"},left_right_arrow:{keywords:["shape","direction","horizontal","sideways"],char:'\u2194\ufe0f',fitzpatrick_scale:!1,category:"symbols"},arrows_counterclockwise:{keywords:["blue-square","sync","cycle"],char:'\u{1f504}',fitzpatrick_scale:!1,category:"symbols"},arrow_right_hook:{keywords:["blue-square","return","rotate","direction"],char:'\u21aa\ufe0f',fitzpatrick_scale:!1,category:"symbols"},leftwards_arrow_with_hook:{keywords:["back","return","blue-square","undo","enter"],char:'\u21a9\ufe0f',fitzpatrick_scale:!1,category:"symbols"},arrow_heading_up:{keywords:["blue-square","direction","top"],char:'\u2934\ufe0f',fitzpatrick_scale:!1,category:"symbols"},arrow_heading_down:{keywords:["blue-square","direction","bottom"],char:'\u2935\ufe0f',fitzpatrick_scale:!1,category:"symbols"},hash:{keywords:["symbol","blue-square","twitter"],char:'#\ufe0f\u20e3',fitzpatrick_scale:!1,category:"symbols"},information_source:{keywords:["blue-square","alphabet","letter"],char:'\u2139\ufe0f',fitzpatrick_scale:!1,category:"symbols"},abc:{keywords:["blue-square","alphabet"],char:'\u{1f524}',fitzpatrick_scale:!1,category:"symbols"},abcd:{keywords:["blue-square","alphabet"],char:'\u{1f521}',fitzpatrick_scale:!1,category:"symbols"},capital_abcd:{keywords:["alphabet","words","blue-square"],char:'\u{1f520}',fitzpatrick_scale:!1,category:"symbols"},symbols:{keywords:["blue-square","music","note","ampersand","percent","glyphs","characters"],char:'\u{1f523}',fitzpatrick_scale:!1,category:"symbols"},musical_note:{keywords:["score","tone","sound"],char:'\u{1f3b5}',fitzpatrick_scale:!1,category:"symbols"},notes:{keywords:["music","score"],char:'\u{1f3b6}',fitzpatrick_scale:!1,category:"symbols"},wavy_dash:{keywords:["draw","line","moustache","mustache","squiggle","scribble"],char:'\u3030\ufe0f',fitzpatrick_scale:!1,category:"symbols"},curly_loop:{keywords:["scribble","draw","shape","squiggle"],char:'\u27b0',fitzpatrick_scale:!1,category:"symbols"},heavy_check_mark:{keywords:["ok","nike","answer","yes","tick"],char:'\u2714\ufe0f',fitzpatrick_scale:!1,category:"symbols"},arrows_clockwise:{keywords:["sync","cycle","round","repeat"],char:'\u{1f503}',fitzpatrick_scale:!1,category:"symbols"},heavy_plus_sign:{keywords:["math","calculation","addition","more","increase"],char:'\u2795',fitzpatrick_scale:!1,category:"symbols"},heavy_minus_sign:{keywords:["math","calculation","subtract","less"],char:'\u2796',fitzpatrick_scale:!1,category:"symbols"},heavy_division_sign:{keywords:["divide","math","calculation"],char:'\u2797',fitzpatrick_scale:!1,category:"symbols"},heavy_multiplication_x:{keywords:["math","calculation"],char:'\u2716\ufe0f',fitzpatrick_scale:!1,category:"symbols"},infinity:{keywords:["forever"],char:'\u267e',fitzpatrick_scale:!1,category:"symbols"},heavy_dollar_sign:{keywords:["money","sales","payment","currency","buck"],char:'\u{1f4b2}',fitzpatrick_scale:!1,category:"symbols"},currency_exchange:{keywords:["money","sales","dollar","travel"],char:'\u{1f4b1}',fitzpatrick_scale:!1,category:"symbols"},copyright:{keywords:["ip","license","circle","law","legal"],char:'\xa9\ufe0f',fitzpatrick_scale:!1,category:"symbols"},registered:{keywords:["alphabet","circle"],char:'\xae\ufe0f',fitzpatrick_scale:!1,category:"symbols"},tm:{keywords:["trademark","brand","law","legal"],char:'\u2122\ufe0f',fitzpatrick_scale:!1,category:"symbols"},end:{keywords:["words","arrow"],char:'\u{1f51a}',fitzpatrick_scale:!1,category:"symbols"},back:{keywords:["arrow","words","return"],char:'\u{1f519}',fitzpatrick_scale:!1,category:"symbols"},on:{keywords:["arrow","words"],char:'\u{1f51b}',fitzpatrick_scale:!1,category:"symbols"},top:{keywords:["words","blue-square"],char:'\u{1f51d}',fitzpatrick_scale:!1,category:"symbols"},soon:{keywords:["arrow","words"],char:'\u{1f51c}',fitzpatrick_scale:!1,category:"symbols"},ballot_box_with_check:{keywords:["ok","agree","confirm","black-square","vote","election","yes","tick"],char:'\u2611\ufe0f',fitzpatrick_scale:!1,category:"symbols"},radio_button:{keywords:["input","old","music","circle"],char:'\u{1f518}',fitzpatrick_scale:!1,category:"symbols"},white_circle:{keywords:["shape","round"],char:'\u26aa',fitzpatrick_scale:!1,category:"symbols"},black_circle:{keywords:["shape","button","round"],char:'\u26ab',fitzpatrick_scale:!1,category:"symbols"},red_circle:{keywords:["shape","error","danger"],char:'\u{1f534}',fitzpatrick_scale:!1,category:"symbols"},large_blue_circle:{keywords:["shape","icon","button"],char:'\u{1f535}',fitzpatrick_scale:!1,category:"symbols"},small_orange_diamond:{keywords:["shape","jewel","gem"],char:'\u{1f538}',fitzpatrick_scale:!1,category:"symbols"},small_blue_diamond:{keywords:["shape","jewel","gem"],char:'\u{1f539}',fitzpatrick_scale:!1,category:"symbols"},large_orange_diamond:{keywords:["shape","jewel","gem"],char:'\u{1f536}',fitzpatrick_scale:!1,category:"symbols"},large_blue_diamond:{keywords:["shape","jewel","gem"],char:'\u{1f537}',fitzpatrick_scale:!1,category:"symbols"},small_red_triangle:{keywords:["shape","direction","up","top"],char:'\u{1f53a}',fitzpatrick_scale:!1,category:"symbols"},black_small_square:{keywords:["shape","icon"],char:'\u25aa\ufe0f',fitzpatrick_scale:!1,category:"symbols"},white_small_square:{keywords:["shape","icon"],char:'\u25ab\ufe0f',fitzpatrick_scale:!1,category:"symbols"},black_large_square:{keywords:["shape","icon","button"],char:'\u2b1b',fitzpatrick_scale:!1,category:"symbols"},white_large_square:{keywords:["shape","icon","stone","button"],char:'\u2b1c',fitzpatrick_scale:!1,category:"symbols"},small_red_triangle_down:{keywords:["shape","direction","bottom"],char:'\u{1f53b}',fitzpatrick_scale:!1,category:"symbols"},black_medium_square:{keywords:["shape","button","icon"],char:'\u25fc\ufe0f',fitzpatrick_scale:!1,category:"symbols"},white_medium_square:{keywords:["shape","stone","icon"],char:'\u25fb\ufe0f',fitzpatrick_scale:!1,category:"symbols"},black_medium_small_square:{keywords:["icon","shape","button"],char:'\u25fe',fitzpatrick_scale:!1,category:"symbols"},white_medium_small_square:{keywords:["shape","stone","icon","button"],char:'\u25fd',fitzpatrick_scale:!1,category:"symbols"},black_square_button:{keywords:["shape","input","frame"],char:'\u{1f532}',fitzpatrick_scale:!1,category:"symbols"},white_square_button:{keywords:["shape","input"],char:'\u{1f533}',fitzpatrick_scale:!1,category:"symbols"},speaker:{keywords:["sound","volume","silence","broadcast"],char:'\u{1f508}',fitzpatrick_scale:!1,category:"symbols"},sound:{keywords:["volume","speaker","broadcast"],char:'\u{1f509}',fitzpatrick_scale:!1,category:"symbols"},loud_sound:{keywords:["volume","noise","noisy","speaker","broadcast"],char:'\u{1f50a}',fitzpatrick_scale:!1,category:"symbols"},mute:{keywords:["sound","volume","silence","quiet"],char:'\u{1f507}',fitzpatrick_scale:!1,category:"symbols"},mega:{keywords:["sound","speaker","volume"],char:'\u{1f4e3}',fitzpatrick_scale:!1,category:"symbols"},loudspeaker:{keywords:["volume","sound"],char:'\u{1f4e2}',fitzpatrick_scale:!1,category:"symbols"},bell:{keywords:["sound","notification","christmas","xmas","chime"],char:'\u{1f514}',fitzpatrick_scale:!1,category:"symbols"},no_bell:{keywords:["sound","volume","mute","quiet","silent"],char:'\u{1f515}',fitzpatrick_scale:!1,category:"symbols"},black_joker:{keywords:["poker","cards","game","play","magic"],char:'\u{1f0cf}',fitzpatrick_scale:!1,category:"symbols"},mahjong:{keywords:["game","play","chinese","kanji"],char:'\u{1f004}',fitzpatrick_scale:!1,category:"symbols"},spades:{keywords:["poker","cards","suits","magic"],char:'\u2660\ufe0f',fitzpatrick_scale:!1,category:"symbols"},clubs:{keywords:["poker","cards","magic","suits"],char:'\u2663\ufe0f',fitzpatrick_scale:!1,category:"symbols"},hearts:{keywords:["poker","cards","magic","suits"],char:'\u2665\ufe0f',fitzpatrick_scale:!1,category:"symbols"},diamonds:{keywords:["poker","cards","magic","suits"],char:'\u2666\ufe0f',fitzpatrick_scale:!1,category:"symbols"},flower_playing_cards:{keywords:["game","sunset","red"],char:'\u{1f3b4}',fitzpatrick_scale:!1,category:"symbols"},thought_balloon:{keywords:["bubble","cloud","speech","thinking","dream"],char:'\u{1f4ad}',fitzpatrick_scale:!1,category:"symbols"},right_anger_bubble:{keywords:["caption","speech","thinking","mad"],char:'\u{1f5ef}',fitzpatrick_scale:!1,category:"symbols"},speech_balloon:{keywords:["bubble","words","message","talk","chatting"],char:'\u{1f4ac}',fitzpatrick_scale:!1,category:"symbols"},left_speech_bubble:{keywords:["words","message","talk","chatting"],char:'\u{1f5e8}',fitzpatrick_scale:!1,category:"symbols"},clock1:{keywords:["time","late","early","schedule"],char:'\u{1f550}',fitzpatrick_scale:!1,category:"symbols"},clock2:{keywords:["time","late","early","schedule"],char:'\u{1f551}',fitzpatrick_scale:!1,category:"symbols"},clock3:{keywords:["time","late","early","schedule"],char:'\u{1f552}',fitzpatrick_scale:!1,category:"symbols"},clock4:{keywords:["time","late","early","schedule"],char:'\u{1f553}',fitzpatrick_scale:!1,category:"symbols"},clock5:{keywords:["time","late","early","schedule"],char:'\u{1f554}',fitzpatrick_scale:!1,category:"symbols"},clock6:{keywords:["time","late","early","schedule","dawn","dusk"],char:'\u{1f555}',fitzpatrick_scale:!1,category:"symbols"},clock7:{keywords:["time","late","early","schedule"],char:'\u{1f556}',fitzpatrick_scale:!1,category:"symbols"},clock8:{keywords:["time","late","early","schedule"],char:'\u{1f557}',fitzpatrick_scale:!1,category:"symbols"},clock9:{keywords:["time","late","early","schedule"],char:'\u{1f558}',fitzpatrick_scale:!1,category:"symbols"},clock10:{keywords:["time","late","early","schedule"],char:'\u{1f559}',fitzpatrick_scale:!1,category:"symbols"},clock11:{keywords:["time","late","early","schedule"],char:'\u{1f55a}',fitzpatrick_scale:!1,category:"symbols"},clock12:{keywords:["time","noon","midnight","midday","late","early","schedule"],char:'\u{1f55b}',fitzpatrick_scale:!1,category:"symbols"},clock130:{keywords:["time","late","early","schedule"],char:'\u{1f55c}',fitzpatrick_scale:!1,category:"symbols"},clock230:{keywords:["time","late","early","schedule"],char:'\u{1f55d}',fitzpatrick_scale:!1,category:"symbols"},clock330:{keywords:["time","late","early","schedule"],char:'\u{1f55e}',fitzpatrick_scale:!1,category:"symbols"},clock430:{keywords:["time","late","early","schedule"],char:'\u{1f55f}',fitzpatrick_scale:!1,category:"symbols"},clock530:{keywords:["time","late","early","schedule"],char:'\u{1f560}',fitzpatrick_scale:!1,category:"symbols"},clock630:{keywords:["time","late","early","schedule"],char:'\u{1f561}',fitzpatrick_scale:!1,category:"symbols"},clock730:{keywords:["time","late","early","schedule"],char:'\u{1f562}',fitzpatrick_scale:!1,category:"symbols"},clock830:{keywords:["time","late","early","schedule"],char:'\u{1f563}',fitzpatrick_scale:!1,category:"symbols"},clock930:{keywords:["time","late","early","schedule"],char:'\u{1f564}',fitzpatrick_scale:!1,category:"symbols"},clock1030:{keywords:["time","late","early","schedule"],char:'\u{1f565}',fitzpatrick_scale:!1,category:"symbols"},clock1130:{keywords:["time","late","early","schedule"],char:'\u{1f566}',fitzpatrick_scale:!1,category:"symbols"},clock1230:{keywords:["time","late","early","schedule"],char:'\u{1f567}',fitzpatrick_scale:!1,category:"symbols"},afghanistan:{keywords:["af","flag","nation","country","banner"],char:'\u{1f1e6}\u{1f1eb}',fitzpatrick_scale:!1,category:"flags"},aland_islands:{keywords:["\xc5land","islands","flag","nation","country","banner"],char:'\u{1f1e6}\u{1f1fd}',fitzpatrick_scale:!1,category:"flags"},albania:{keywords:["al","flag","nation","country","banner"],char:'\u{1f1e6}\u{1f1f1}',fitzpatrick_scale:!1,category:"flags"},algeria:{keywords:["dz","flag","nation","country","banner"],char:'\u{1f1e9}\u{1f1ff}',fitzpatrick_scale:!1,category:"flags"},american_samoa:{keywords:["american","ws","flag","nation","country","banner"],char:'\u{1f1e6}\u{1f1f8}',fitzpatrick_scale:!1,category:"flags"},andorra:{keywords:["ad","flag","nation","country","banner"],char:'\u{1f1e6}\u{1f1e9}',fitzpatrick_scale:!1,category:"flags"},angola:{keywords:["ao","flag","nation","country","banner"],char:'\u{1f1e6}\u{1f1f4}',fitzpatrick_scale:!1,category:"flags"},anguilla:{keywords:["ai","flag","nation","country","banner"],char:'\u{1f1e6}\u{1f1ee}',fitzpatrick_scale:!1,category:"flags"},antarctica:{keywords:["aq","flag","nation","country","banner"],char:'\u{1f1e6}\u{1f1f6}',fitzpatrick_scale:!1,category:"flags"},antigua_barbuda:{keywords:["antigua","barbuda","flag","nation","country","banner"],char:'\u{1f1e6}\u{1f1ec}',fitzpatrick_scale:!1,category:"flags"},argentina:{keywords:["ar","flag","nation","country","banner"],char:'\u{1f1e6}\u{1f1f7}',fitzpatrick_scale:!1,category:"flags"},armenia:{keywords:["am","flag","nation","country","banner"],char:'\u{1f1e6}\u{1f1f2}',fitzpatrick_scale:!1,category:"flags"},aruba:{keywords:["aw","flag","nation","country","banner"],char:'\u{1f1e6}\u{1f1fc}',fitzpatrick_scale:!1,category:"flags"},australia:{keywords:["au","flag","nation","country","banner"],char:'\u{1f1e6}\u{1f1fa}',fitzpatrick_scale:!1,category:"flags"},austria:{keywords:["at","flag","nation","country","banner"],char:'\u{1f1e6}\u{1f1f9}',fitzpatrick_scale:!1,category:"flags"},azerbaijan:{keywords:["az","flag","nation","country","banner"],char:'\u{1f1e6}\u{1f1ff}',fitzpatrick_scale:!1,category:"flags"},bahamas:{keywords:["bs","flag","nation","country","banner"],char:'\u{1f1e7}\u{1f1f8}',fitzpatrick_scale:!1,category:"flags"},bahrain:{keywords:["bh","flag","nation","country","banner"],char:'\u{1f1e7}\u{1f1ed}',fitzpatrick_scale:!1,category:"flags"},bangladesh:{keywords:["bd","flag","nation","country","banner"],char:'\u{1f1e7}\u{1f1e9}',fitzpatrick_scale:!1,category:"flags"},barbados:{keywords:["bb","flag","nation","country","banner"],char:'\u{1f1e7}\u{1f1e7}',fitzpatrick_scale:!1,category:"flags"},belarus:{keywords:["by","flag","nation","country","banner"],char:'\u{1f1e7}\u{1f1fe}',fitzpatrick_scale:!1,category:"flags"},belgium:{keywords:["be","flag","nation","country","banner"],char:'\u{1f1e7}\u{1f1ea}',fitzpatrick_scale:!1,category:"flags"},belize:{keywords:["bz","flag","nation","country","banner"],char:'\u{1f1e7}\u{1f1ff}',fitzpatrick_scale:!1,category:"flags"},benin:{keywords:["bj","flag","nation","country","banner"],char:'\u{1f1e7}\u{1f1ef}',fitzpatrick_scale:!1,category:"flags"},bermuda:{keywords:["bm","flag","nation","country","banner"],char:'\u{1f1e7}\u{1f1f2}',fitzpatrick_scale:!1,category:"flags"},bhutan:{keywords:["bt","flag","nation","country","banner"],char:'\u{1f1e7}\u{1f1f9}',fitzpatrick_scale:!1,category:"flags"},bolivia:{keywords:["bo","flag","nation","country","banner"],char:'\u{1f1e7}\u{1f1f4}',fitzpatrick_scale:!1,category:"flags"},caribbean_netherlands:{keywords:["bonaire","flag","nation","country","banner"],char:'\u{1f1e7}\u{1f1f6}',fitzpatrick_scale:!1,category:"flags"},bosnia_herzegovina:{keywords:["bosnia","herzegovina","flag","nation","country","banner"],char:'\u{1f1e7}\u{1f1e6}',fitzpatrick_scale:!1,category:"flags"},botswana:{keywords:["bw","flag","nation","country","banner"],char:'\u{1f1e7}\u{1f1fc}',fitzpatrick_scale:!1,category:"flags"},brazil:{keywords:["br","flag","nation","country","banner"],char:'\u{1f1e7}\u{1f1f7}',fitzpatrick_scale:!1,category:"flags"},british_indian_ocean_territory:{keywords:["british","indian","ocean","territory","flag","nation","country","banner"],char:'\u{1f1ee}\u{1f1f4}',fitzpatrick_scale:!1,category:"flags"},british_virgin_islands:{keywords:["british","virgin","islands","bvi","flag","nation","country","banner"],char:'\u{1f1fb}\u{1f1ec}',fitzpatrick_scale:!1,category:"flags"},brunei:{keywords:["bn","darussalam","flag","nation","country","banner"],char:'\u{1f1e7}\u{1f1f3}',fitzpatrick_scale:!1,category:"flags"},bulgaria:{keywords:["bg","flag","nation","country","banner"],char:'\u{1f1e7}\u{1f1ec}',fitzpatrick_scale:!1,category:"flags"},burkina_faso:{keywords:["burkina","faso","flag","nation","country","banner"],char:'\u{1f1e7}\u{1f1eb}',fitzpatrick_scale:!1,category:"flags"},burundi:{keywords:["bi","flag","nation","country","banner"],char:'\u{1f1e7}\u{1f1ee}',fitzpatrick_scale:!1,category:"flags"},cape_verde:{keywords:["cabo","verde","flag","nation","country","banner"],char:'\u{1f1e8}\u{1f1fb}',fitzpatrick_scale:!1,category:"flags"},cambodia:{keywords:["kh","flag","nation","country","banner"],char:'\u{1f1f0}\u{1f1ed}',fitzpatrick_scale:!1,category:"flags"},cameroon:{keywords:["cm","flag","nation","country","banner"],char:'\u{1f1e8}\u{1f1f2}',fitzpatrick_scale:!1,category:"flags"},canada:{keywords:["ca","flag","nation","country","banner"],char:'\u{1f1e8}\u{1f1e6}',fitzpatrick_scale:!1,category:"flags"},canary_islands:{keywords:["canary","islands","flag","nation","country","banner"],char:'\u{1f1ee}\u{1f1e8}',fitzpatrick_scale:!1,category:"flags"},cayman_islands:{keywords:["cayman","islands","flag","nation","country","banner"],char:'\u{1f1f0}\u{1f1fe}',fitzpatrick_scale:!1,category:"flags"},central_african_republic:{keywords:["central","african","republic","flag","nation","country","banner"],char:'\u{1f1e8}\u{1f1eb}',fitzpatrick_scale:!1,category:"flags"},chad:{keywords:["td","flag","nation","country","banner"],char:'\u{1f1f9}\u{1f1e9}',fitzpatrick_scale:!1,category:"flags"},chile:{keywords:["flag","nation","country","banner"],char:'\u{1f1e8}\u{1f1f1}',fitzpatrick_scale:!1,category:"flags"},cn:{keywords:["china","chinese","prc","flag","country","nation","banner"],char:'\u{1f1e8}\u{1f1f3}',fitzpatrick_scale:!1,category:"flags"},christmas_island:{keywords:["christmas","island","flag","nation","country","banner"],char:'\u{1f1e8}\u{1f1fd}',fitzpatrick_scale:!1,category:"flags"},cocos_islands:{keywords:["cocos","keeling","islands","flag","nation","country","banner"],char:'\u{1f1e8}\u{1f1e8}',fitzpatrick_scale:!1,category:"flags"},colombia:{keywords:["co","flag","nation","country","banner"],char:'\u{1f1e8}\u{1f1f4}',fitzpatrick_scale:!1,category:"flags"},comoros:{keywords:["km","flag","nation","country","banner"],char:'\u{1f1f0}\u{1f1f2}',fitzpatrick_scale:!1,category:"flags"},congo_brazzaville:{keywords:["congo","flag","nation","country","banner"],char:'\u{1f1e8}\u{1f1ec}',fitzpatrick_scale:!1,category:"flags"},congo_kinshasa:{keywords:["congo","democratic","republic","flag","nation","country","banner"],char:'\u{1f1e8}\u{1f1e9}',fitzpatrick_scale:!1,category:"flags"},cook_islands:{keywords:["cook","islands","flag","nation","country","banner"],char:'\u{1f1e8}\u{1f1f0}',fitzpatrick_scale:!1,category:"flags"},costa_rica:{keywords:["costa","rica","flag","nation","country","banner"],char:'\u{1f1e8}\u{1f1f7}',fitzpatrick_scale:!1,category:"flags"},croatia:{keywords:["hr","flag","nation","country","banner"],char:'\u{1f1ed}\u{1f1f7}',fitzpatrick_scale:!1,category:"flags"},cuba:{keywords:["cu","flag","nation","country","banner"],char:'\u{1f1e8}\u{1f1fa}',fitzpatrick_scale:!1,category:"flags"},curacao:{keywords:["cura\xe7ao","flag","nation","country","banner"],char:'\u{1f1e8}\u{1f1fc}',fitzpatrick_scale:!1,category:"flags"},cyprus:{keywords:["cy","flag","nation","country","banner"],char:'\u{1f1e8}\u{1f1fe}',fitzpatrick_scale:!1,category:"flags"},czech_republic:{keywords:["cz","flag","nation","country","banner"],char:'\u{1f1e8}\u{1f1ff}',fitzpatrick_scale:!1,category:"flags"},denmark:{keywords:["dk","flag","nation","country","banner"],char:'\u{1f1e9}\u{1f1f0}',fitzpatrick_scale:!1,category:"flags"},djibouti:{keywords:["dj","flag","nation","country","banner"],char:'\u{1f1e9}\u{1f1ef}',fitzpatrick_scale:!1,category:"flags"},dominica:{keywords:["dm","flag","nation","country","banner"],char:'\u{1f1e9}\u{1f1f2}',fitzpatrick_scale:!1,category:"flags"},dominican_republic:{keywords:["dominican","republic","flag","nation","country","banner"],char:'\u{1f1e9}\u{1f1f4}',fitzpatrick_scale:!1,category:"flags"},ecuador:{keywords:["ec","flag","nation","country","banner"],char:'\u{1f1ea}\u{1f1e8}',fitzpatrick_scale:!1,category:"flags"},egypt:{keywords:["eg","flag","nation","country","banner"],char:'\u{1f1ea}\u{1f1ec}',fitzpatrick_scale:!1,category:"flags"},el_salvador:{keywords:["el","salvador","flag","nation","country","banner"],char:'\u{1f1f8}\u{1f1fb}',fitzpatrick_scale:!1,category:"flags"},equatorial_guinea:{keywords:["equatorial","gn","flag","nation","country","banner"],char:'\u{1f1ec}\u{1f1f6}',fitzpatrick_scale:!1,category:"flags"},eritrea:{keywords:["er","flag","nation","country","banner"],char:'\u{1f1ea}\u{1f1f7}',fitzpatrick_scale:!1,category:"flags"},estonia:{keywords:["ee","flag","nation","country","banner"],char:'\u{1f1ea}\u{1f1ea}',fitzpatrick_scale:!1,category:"flags"},ethiopia:{keywords:["et","flag","nation","country","banner"],char:'\u{1f1ea}\u{1f1f9}',fitzpatrick_scale:!1,category:"flags"},eu:{keywords:["european","union","flag","banner"],char:'\u{1f1ea}\u{1f1fa}',fitzpatrick_scale:!1,category:"flags"},falkland_islands:{keywords:["falkland","islands","malvinas","flag","nation","country","banner"],char:'\u{1f1eb}\u{1f1f0}',fitzpatrick_scale:!1,category:"flags"},faroe_islands:{keywords:["faroe","islands","flag","nation","country","banner"],char:'\u{1f1eb}\u{1f1f4}',fitzpatrick_scale:!1,category:"flags"},fiji:{keywords:["fj","flag","nation","country","banner"],char:'\u{1f1eb}\u{1f1ef}',fitzpatrick_scale:!1,category:"flags"},finland:{keywords:["fi","flag","nation","country","banner"],char:'\u{1f1eb}\u{1f1ee}',fitzpatrick_scale:!1,category:"flags"},fr:{keywords:["banner","flag","nation","france","french","country"],char:'\u{1f1eb}\u{1f1f7}',fitzpatrick_scale:!1,category:"flags"},french_guiana:{keywords:["french","guiana","flag","nation","country","banner"],char:'\u{1f1ec}\u{1f1eb}',fitzpatrick_scale:!1,category:"flags"},french_polynesia:{keywords:["french","polynesia","flag","nation","country","banner"],char:'\u{1f1f5}\u{1f1eb}',fitzpatrick_scale:!1,category:"flags"},french_southern_territories:{keywords:["french","southern","territories","flag","nation","country","banner"],char:'\u{1f1f9}\u{1f1eb}',fitzpatrick_scale:!1,category:"flags"},gabon:{keywords:["ga","flag","nation","country","banner"],char:'\u{1f1ec}\u{1f1e6}',fitzpatrick_scale:!1,category:"flags"},gambia:{keywords:["gm","flag","nation","country","banner"],char:'\u{1f1ec}\u{1f1f2}',fitzpatrick_scale:!1,category:"flags"},georgia:{keywords:["ge","flag","nation","country","banner"],char:'\u{1f1ec}\u{1f1ea}',fitzpatrick_scale:!1,category:"flags"},de:{keywords:["german","nation","flag","country","banner"],char:'\u{1f1e9}\u{1f1ea}',fitzpatrick_scale:!1,category:"flags"},ghana:{keywords:["gh","flag","nation","country","banner"],char:'\u{1f1ec}\u{1f1ed}',fitzpatrick_scale:!1,category:"flags"},gibraltar:{keywords:["gi","flag","nation","country","banner"],char:'\u{1f1ec}\u{1f1ee}',fitzpatrick_scale:!1,category:"flags"},greece:{keywords:["gr","flag","nation","country","banner"],char:'\u{1f1ec}\u{1f1f7}',fitzpatrick_scale:!1,category:"flags"},greenland:{keywords:["gl","flag","nation","country","banner"],char:'\u{1f1ec}\u{1f1f1}',fitzpatrick_scale:!1,category:"flags"},grenada:{keywords:["gd","flag","nation","country","banner"],char:'\u{1f1ec}\u{1f1e9}',fitzpatrick_scale:!1,category:"flags"},guadeloupe:{keywords:["gp","flag","nation","country","banner"],char:'\u{1f1ec}\u{1f1f5}',fitzpatrick_scale:!1,category:"flags"},guam:{keywords:["gu","flag","nation","country","banner"],char:'\u{1f1ec}\u{1f1fa}',fitzpatrick_scale:!1,category:"flags"},guatemala:{keywords:["gt","flag","nation","country","banner"],char:'\u{1f1ec}\u{1f1f9}',fitzpatrick_scale:!1,category:"flags"},guernsey:{keywords:["gg","flag","nation","country","banner"],char:'\u{1f1ec}\u{1f1ec}',fitzpatrick_scale:!1,category:"flags"},guinea:{keywords:["gn","flag","nation","country","banner"],char:'\u{1f1ec}\u{1f1f3}',fitzpatrick_scale:!1,category:"flags"},guinea_bissau:{keywords:["gw","bissau","flag","nation","country","banner"],char:'\u{1f1ec}\u{1f1fc}',fitzpatrick_scale:!1,category:"flags"},guyana:{keywords:["gy","flag","nation","country","banner"],char:'\u{1f1ec}\u{1f1fe}',fitzpatrick_scale:!1,category:"flags"},haiti:{keywords:["ht","flag","nation","country","banner"],char:'\u{1f1ed}\u{1f1f9}',fitzpatrick_scale:!1,category:"flags"},honduras:{keywords:["hn","flag","nation","country","banner"],char:'\u{1f1ed}\u{1f1f3}',fitzpatrick_scale:!1,category:"flags"},hong_kong:{keywords:["hong","kong","flag","nation","country","banner"],char:'\u{1f1ed}\u{1f1f0}',fitzpatrick_scale:!1,category:"flags"},hungary:{keywords:["hu","flag","nation","country","banner"],char:'\u{1f1ed}\u{1f1fa}',fitzpatrick_scale:!1,category:"flags"},iceland:{keywords:["is","flag","nation","country","banner"],char:'\u{1f1ee}\u{1f1f8}',fitzpatrick_scale:!1,category:"flags"},india:{keywords:["in","flag","nation","country","banner"],char:'\u{1f1ee}\u{1f1f3}',fitzpatrick_scale:!1,category:"flags"},indonesia:{keywords:["flag","nation","country","banner"],char:'\u{1f1ee}\u{1f1e9}',fitzpatrick_scale:!1,category:"flags"},iran:{keywords:["iran,","islamic","republic","flag","nation","country","banner"],char:'\u{1f1ee}\u{1f1f7}',fitzpatrick_scale:!1,category:"flags"},iraq:{keywords:["iq","flag","nation","country","banner"],char:'\u{1f1ee}\u{1f1f6}',fitzpatrick_scale:!1,category:"flags"},ireland:{keywords:["ie","flag","nation","country","banner"],char:'\u{1f1ee}\u{1f1ea}',fitzpatrick_scale:!1,category:"flags"},isle_of_man:{keywords:["isle","man","flag","nation","country","banner"],char:'\u{1f1ee}\u{1f1f2}',fitzpatrick_scale:!1,category:"flags"},israel:{keywords:["il","flag","nation","country","banner"],char:'\u{1f1ee}\u{1f1f1}',fitzpatrick_scale:!1,category:"flags"},it:{keywords:["italy","flag","nation","country","banner"],char:'\u{1f1ee}\u{1f1f9}',fitzpatrick_scale:!1,category:"flags"},cote_divoire:{keywords:["ivory","coast","flag","nation","country","banner"],char:'\u{1f1e8}\u{1f1ee}',fitzpatrick_scale:!1,category:"flags"},jamaica:{keywords:["jm","flag","nation","country","banner"],char:'\u{1f1ef}\u{1f1f2}',fitzpatrick_scale:!1,category:"flags"},jp:{keywords:["japanese","nation","flag","country","banner"],char:'\u{1f1ef}\u{1f1f5}',fitzpatrick_scale:!1,category:"flags"},jersey:{keywords:["je","flag","nation","country","banner"],char:'\u{1f1ef}\u{1f1ea}',fitzpatrick_scale:!1,category:"flags"},jordan:{keywords:["jo","flag","nation","country","banner"],char:'\u{1f1ef}\u{1f1f4}',fitzpatrick_scale:!1,category:"flags"},kazakhstan:{keywords:["kz","flag","nation","country","banner"],char:'\u{1f1f0}\u{1f1ff}',fitzpatrick_scale:!1,category:"flags"},kenya:{keywords:["ke","flag","nation","country","banner"],char:'\u{1f1f0}\u{1f1ea}',fitzpatrick_scale:!1,category:"flags"},kiribati:{keywords:["ki","flag","nation","country","banner"],char:'\u{1f1f0}\u{1f1ee}',fitzpatrick_scale:!1,category:"flags"},kosovo:{keywords:["xk","flag","nation","country","banner"],char:'\u{1f1fd}\u{1f1f0}',fitzpatrick_scale:!1,category:"flags"},kuwait:{keywords:["kw","flag","nation","country","banner"],char:'\u{1f1f0}\u{1f1fc}',fitzpatrick_scale:!1,category:"flags"},kyrgyzstan:{keywords:["kg","flag","nation","country","banner"],char:'\u{1f1f0}\u{1f1ec}',fitzpatrick_scale:!1,category:"flags"},laos:{keywords:["lao","democratic","republic","flag","nation","country","banner"],char:'\u{1f1f1}\u{1f1e6}',fitzpatrick_scale:!1,category:"flags"},latvia:{keywords:["lv","flag","nation","country","banner"],char:'\u{1f1f1}\u{1f1fb}',fitzpatrick_scale:!1,category:"flags"},lebanon:{keywords:["lb","flag","nation","country","banner"],char:'\u{1f1f1}\u{1f1e7}',fitzpatrick_scale:!1,category:"flags"},lesotho:{keywords:["ls","flag","nation","country","banner"],char:'\u{1f1f1}\u{1f1f8}',fitzpatrick_scale:!1,category:"flags"},liberia:{keywords:["lr","flag","nation","country","banner"],char:'\u{1f1f1}\u{1f1f7}',fitzpatrick_scale:!1,category:"flags"},libya:{keywords:["ly","flag","nation","country","banner"],char:'\u{1f1f1}\u{1f1fe}',fitzpatrick_scale:!1,category:"flags"},liechtenstein:{keywords:["li","flag","nation","country","banner"],char:'\u{1f1f1}\u{1f1ee}',fitzpatrick_scale:!1,category:"flags"},lithuania:{keywords:["lt","flag","nation","country","banner"],char:'\u{1f1f1}\u{1f1f9}',fitzpatrick_scale:!1,category:"flags"},luxembourg:{keywords:["lu","flag","nation","country","banner"],char:'\u{1f1f1}\u{1f1fa}',fitzpatrick_scale:!1,category:"flags"},macau:{keywords:["macao","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1f4}',fitzpatrick_scale:!1,category:"flags"},macedonia:{keywords:["macedonia,","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1f0}',fitzpatrick_scale:!1,category:"flags"},madagascar:{keywords:["mg","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1ec}',fitzpatrick_scale:!1,category:"flags"},malawi:{keywords:["mw","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1fc}',fitzpatrick_scale:!1,category:"flags"},malaysia:{keywords:["my","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1fe}',fitzpatrick_scale:!1,category:"flags"},maldives:{keywords:["mv","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1fb}',fitzpatrick_scale:!1,category:"flags"},mali:{keywords:["ml","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1f1}',fitzpatrick_scale:!1,category:"flags"},malta:{keywords:["mt","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1f9}',fitzpatrick_scale:!1,category:"flags"},marshall_islands:{keywords:["marshall","islands","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1ed}',fitzpatrick_scale:!1,category:"flags"},martinique:{keywords:["mq","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1f6}',fitzpatrick_scale:!1,category:"flags"},mauritania:{keywords:["mr","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1f7}',fitzpatrick_scale:!1,category:"flags"},mauritius:{keywords:["mu","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1fa}',fitzpatrick_scale:!1,category:"flags"},mayotte:{keywords:["yt","flag","nation","country","banner"],char:'\u{1f1fe}\u{1f1f9}',fitzpatrick_scale:!1,category:"flags"},mexico:{keywords:["mx","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1fd}',fitzpatrick_scale:!1,category:"flags"},micronesia:{keywords:["micronesia,","federated","states","flag","nation","country","banner"],char:'\u{1f1eb}\u{1f1f2}',fitzpatrick_scale:!1,category:"flags"},moldova:{keywords:["moldova,","republic","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1e9}',fitzpatrick_scale:!1,category:"flags"},monaco:{keywords:["mc","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1e8}',fitzpatrick_scale:!1,category:"flags"},mongolia:{keywords:["mn","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1f3}',fitzpatrick_scale:!1,category:"flags"},montenegro:{keywords:["me","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1ea}',fitzpatrick_scale:!1,category:"flags"},montserrat:{keywords:["ms","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1f8}',fitzpatrick_scale:!1,category:"flags"},morocco:{keywords:["ma","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1e6}',fitzpatrick_scale:!1,category:"flags"},mozambique:{keywords:["mz","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1ff}',fitzpatrick_scale:!1,category:"flags"},myanmar:{keywords:["mm","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1f2}',fitzpatrick_scale:!1,category:"flags"},namibia:{keywords:["na","flag","nation","country","banner"],char:'\u{1f1f3}\u{1f1e6}',fitzpatrick_scale:!1,category:"flags"},nauru:{keywords:["nr","flag","nation","country","banner"],char:'\u{1f1f3}\u{1f1f7}',fitzpatrick_scale:!1,category:"flags"},nepal:{keywords:["np","flag","nation","country","banner"],char:'\u{1f1f3}\u{1f1f5}',fitzpatrick_scale:!1,category:"flags"},netherlands:{keywords:["nl","flag","nation","country","banner"],char:'\u{1f1f3}\u{1f1f1}',fitzpatrick_scale:!1,category:"flags"},new_caledonia:{keywords:["new","caledonia","flag","nation","country","banner"],char:'\u{1f1f3}\u{1f1e8}',fitzpatrick_scale:!1,category:"flags"},new_zealand:{keywords:["new","zealand","flag","nation","country","banner"],char:'\u{1f1f3}\u{1f1ff}',fitzpatrick_scale:!1,category:"flags"},nicaragua:{keywords:["ni","flag","nation","country","banner"],char:'\u{1f1f3}\u{1f1ee}',fitzpatrick_scale:!1,category:"flags"},niger:{keywords:["ne","flag","nation","country","banner"],char:'\u{1f1f3}\u{1f1ea}',fitzpatrick_scale:!1,category:"flags"},nigeria:{keywords:["flag","nation","country","banner"],char:'\u{1f1f3}\u{1f1ec}',fitzpatrick_scale:!1,category:"flags"},niue:{keywords:["nu","flag","nation","country","banner"],char:'\u{1f1f3}\u{1f1fa}',fitzpatrick_scale:!1,category:"flags"},norfolk_island:{keywords:["norfolk","island","flag","nation","country","banner"],char:'\u{1f1f3}\u{1f1eb}',fitzpatrick_scale:!1,category:"flags"},northern_mariana_islands:{keywords:["northern","mariana","islands","flag","nation","country","banner"],char:'\u{1f1f2}\u{1f1f5}',fitzpatrick_scale:!1,category:"flags"},north_korea:{keywords:["north","korea","nation","flag","country","banner"],char:'\u{1f1f0}\u{1f1f5}',fitzpatrick_scale:!1,category:"flags"},norway:{keywords:["no","flag","nation","country","banner"],char:'\u{1f1f3}\u{1f1f4}',fitzpatrick_scale:!1,category:"flags"},oman:{keywords:["om_symbol","flag","nation","country","banner"],char:'\u{1f1f4}\u{1f1f2}',fitzpatrick_scale:!1,category:"flags"},pakistan:{keywords:["pk","flag","nation","country","banner"],char:'\u{1f1f5}\u{1f1f0}',fitzpatrick_scale:!1,category:"flags"},palau:{keywords:["pw","flag","nation","country","banner"],char:'\u{1f1f5}\u{1f1fc}',fitzpatrick_scale:!1,category:"flags"},palestinian_territories:{keywords:["palestine","palestinian","territories","flag","nation","country","banner"],char:'\u{1f1f5}\u{1f1f8}',fitzpatrick_scale:!1,category:"flags"},panama:{keywords:["pa","flag","nation","country","banner"],char:'\u{1f1f5}\u{1f1e6}',fitzpatrick_scale:!1,category:"flags"},papua_new_guinea:{keywords:["papua","new","guinea","flag","nation","country","banner"],char:'\u{1f1f5}\u{1f1ec}',fitzpatrick_scale:!1,category:"flags"},paraguay:{keywords:["py","flag","nation","country","banner"],char:'\u{1f1f5}\u{1f1fe}',fitzpatrick_scale:!1,category:"flags"},peru:{keywords:["pe","flag","nation","country","banner"],char:'\u{1f1f5}\u{1f1ea}',fitzpatrick_scale:!1,category:"flags"},philippines:{keywords:["ph","flag","nation","country","banner"],char:'\u{1f1f5}\u{1f1ed}',fitzpatrick_scale:!1,category:"flags"},pitcairn_islands:{keywords:["pitcairn","flag","nation","country","banner"],char:'\u{1f1f5}\u{1f1f3}',fitzpatrick_scale:!1,category:"flags"},poland:{keywords:["pl","flag","nation","country","banner"],char:'\u{1f1f5}\u{1f1f1}',fitzpatrick_scale:!1,category:"flags"},portugal:{keywords:["pt","flag","nation","country","banner"],char:'\u{1f1f5}\u{1f1f9}',fitzpatrick_scale:!1,category:"flags"},puerto_rico:{keywords:["puerto","rico","flag","nation","country","banner"],char:'\u{1f1f5}\u{1f1f7}',fitzpatrick_scale:!1,category:"flags"},qatar:{keywords:["qa","flag","nation","country","banner"],char:'\u{1f1f6}\u{1f1e6}',fitzpatrick_scale:!1,category:"flags"},reunion:{keywords:["r\xe9union","flag","nation","country","banner"],char:'\u{1f1f7}\u{1f1ea}',fitzpatrick_scale:!1,category:"flags"},romania:{keywords:["ro","flag","nation","country","banner"],char:'\u{1f1f7}\u{1f1f4}',fitzpatrick_scale:!1,category:"flags"},ru:{keywords:["russian","federation","flag","nation","country","banner"],char:'\u{1f1f7}\u{1f1fa}',fitzpatrick_scale:!1,category:"flags"},rwanda:{keywords:["rw","flag","nation","country","banner"],char:'\u{1f1f7}\u{1f1fc}',fitzpatrick_scale:!1,category:"flags"},st_barthelemy:{keywords:["saint","barth\xe9lemy","flag","nation","country","banner"],char:'\u{1f1e7}\u{1f1f1}',fitzpatrick_scale:!1,category:"flags"},st_helena:{keywords:["saint","helena","ascension","tristan","cunha","flag","nation","country","banner"],char:'\u{1f1f8}\u{1f1ed}',fitzpatrick_scale:!1,category:"flags"},st_kitts_nevis:{keywords:["saint","kitts","nevis","flag","nation","country","banner"],char:'\u{1f1f0}\u{1f1f3}',fitzpatrick_scale:!1,category:"flags"},st_lucia:{keywords:["saint","lucia","flag","nation","country","banner"],char:'\u{1f1f1}\u{1f1e8}',fitzpatrick_scale:!1,category:"flags"},st_pierre_miquelon:{keywords:["saint","pierre","miquelon","flag","nation","country","banner"],char:'\u{1f1f5}\u{1f1f2}',fitzpatrick_scale:!1,category:"flags"},st_vincent_grenadines:{keywords:["saint","vincent","grenadines","flag","nation","country","banner"],char:'\u{1f1fb}\u{1f1e8}',fitzpatrick_scale:!1,category:"flags"},samoa:{keywords:["ws","flag","nation","country","banner"],char:'\u{1f1fc}\u{1f1f8}',fitzpatrick_scale:!1,category:"flags"},san_marino:{keywords:["san","marino","flag","nation","country","banner"],char:'\u{1f1f8}\u{1f1f2}',fitzpatrick_scale:!1,category:"flags"},sao_tome_principe:{keywords:["sao","tome","principe","flag","nation","country","banner"],char:'\u{1f1f8}\u{1f1f9}',fitzpatrick_scale:!1,category:"flags"},saudi_arabia:{keywords:["flag","nation","country","banner"],char:'\u{1f1f8}\u{1f1e6}',fitzpatrick_scale:!1,category:"flags"},senegal:{keywords:["sn","flag","nation","country","banner"],char:'\u{1f1f8}\u{1f1f3}',fitzpatrick_scale:!1,category:"flags"},serbia:{keywords:["rs","flag","nation","country","banner"],char:'\u{1f1f7}\u{1f1f8}',fitzpatrick_scale:!1,category:"flags"},seychelles:{keywords:["sc","flag","nation","country","banner"],char:'\u{1f1f8}\u{1f1e8}',fitzpatrick_scale:!1,category:"flags"},sierra_leone:{keywords:["sierra","leone","flag","nation","country","banner"],char:'\u{1f1f8}\u{1f1f1}',fitzpatrick_scale:!1,category:"flags"},singapore:{keywords:["sg","flag","nation","country","banner"],char:'\u{1f1f8}\u{1f1ec}',fitzpatrick_scale:!1,category:"flags"},sint_maarten:{keywords:["sint","maarten","dutch","flag","nation","country","banner"],char:'\u{1f1f8}\u{1f1fd}',fitzpatrick_scale:!1,category:"flags"},slovakia:{keywords:["sk","flag","nation","country","banner"],char:'\u{1f1f8}\u{1f1f0}',fitzpatrick_scale:!1,category:"flags"},slovenia:{keywords:["si","flag","nation","country","banner"],char:'\u{1f1f8}\u{1f1ee}',fitzpatrick_scale:!1,category:"flags"},solomon_islands:{keywords:["solomon","islands","flag","nation","country","banner"],char:'\u{1f1f8}\u{1f1e7}',fitzpatrick_scale:!1,category:"flags"},somalia:{keywords:["so","flag","nation","country","banner"],char:'\u{1f1f8}\u{1f1f4}',fitzpatrick_scale:!1,category:"flags"},south_africa:{keywords:["south","africa","flag","nation","country","banner"],char:'\u{1f1ff}\u{1f1e6}',fitzpatrick_scale:!1,category:"flags"},south_georgia_south_sandwich_islands:{keywords:["south","georgia","sandwich","islands","flag","nation","country","banner"],char:'\u{1f1ec}\u{1f1f8}',fitzpatrick_scale:!1,category:"flags"},kr:{keywords:["south","korea","nation","flag","country","banner"],char:'\u{1f1f0}\u{1f1f7}',fitzpatrick_scale:!1,category:"flags"},south_sudan:{keywords:["south","sd","flag","nation","country","banner"],char:'\u{1f1f8}\u{1f1f8}',fitzpatrick_scale:!1,category:"flags"},es:{keywords:["spain","flag","nation","country","banner"],char:'\u{1f1ea}\u{1f1f8}',fitzpatrick_scale:!1,category:"flags"},sri_lanka:{keywords:["sri","lanka","flag","nation","country","banner"],char:'\u{1f1f1}\u{1f1f0}',fitzpatrick_scale:!1,category:"flags"},sudan:{keywords:["sd","flag","nation","country","banner"],char:'\u{1f1f8}\u{1f1e9}',fitzpatrick_scale:!1,category:"flags"},suriname:{keywords:["sr","flag","nation","country","banner"],char:'\u{1f1f8}\u{1f1f7}',fitzpatrick_scale:!1,category:"flags"},swaziland:{keywords:["sz","flag","nation","country","banner"],char:'\u{1f1f8}\u{1f1ff}',fitzpatrick_scale:!1,category:"flags"},sweden:{keywords:["se","flag","nation","country","banner"],char:'\u{1f1f8}\u{1f1ea}',fitzpatrick_scale:!1,category:"flags"},switzerland:{keywords:["ch","flag","nation","country","banner"],char:'\u{1f1e8}\u{1f1ed}',fitzpatrick_scale:!1,category:"flags"},syria:{keywords:["syrian","arab","republic","flag","nation","country","banner"],char:'\u{1f1f8}\u{1f1fe}',fitzpatrick_scale:!1,category:"flags"},taiwan:{keywords:["tw","flag","nation","country","banner"],char:'\u{1f1f9}\u{1f1fc}',fitzpatrick_scale:!1,category:"flags"},tajikistan:{keywords:["tj","flag","nation","country","banner"],char:'\u{1f1f9}\u{1f1ef}',fitzpatrick_scale:!1,category:"flags"},tanzania:{keywords:["tanzania,","united","republic","flag","nation","country","banner"],char:'\u{1f1f9}\u{1f1ff}',fitzpatrick_scale:!1,category:"flags"},thailand:{keywords:["th","flag","nation","country","banner"],char:'\u{1f1f9}\u{1f1ed}',fitzpatrick_scale:!1,category:"flags"},timor_leste:{keywords:["timor","leste","flag","nation","country","banner"],char:'\u{1f1f9}\u{1f1f1}',fitzpatrick_scale:!1,category:"flags"},togo:{keywords:["tg","flag","nation","country","banner"],char:'\u{1f1f9}\u{1f1ec}',fitzpatrick_scale:!1,category:"flags"},tokelau:{keywords:["tk","flag","nation","country","banner"],char:'\u{1f1f9}\u{1f1f0}',fitzpatrick_scale:!1,category:"flags"},tonga:{keywords:["to","flag","nation","country","banner"],char:'\u{1f1f9}\u{1f1f4}',fitzpatrick_scale:!1,category:"flags"},trinidad_tobago:{keywords:["trinidad","tobago","flag","nation","country","banner"],char:'\u{1f1f9}\u{1f1f9}',fitzpatrick_scale:!1,category:"flags"},tunisia:{keywords:["tn","flag","nation","country","banner"],char:'\u{1f1f9}\u{1f1f3}',fitzpatrick_scale:!1,category:"flags"},tr:{keywords:["turkey","flag","nation","country","banner"],char:'\u{1f1f9}\u{1f1f7}',fitzpatrick_scale:!1,category:"flags"},turkmenistan:{keywords:["flag","nation","country","banner"],char:'\u{1f1f9}\u{1f1f2}',fitzpatrick_scale:!1,category:"flags"},turks_caicos_islands:{keywords:["turks","caicos","islands","flag","nation","country","banner"],char:'\u{1f1f9}\u{1f1e8}',fitzpatrick_scale:!1,category:"flags"},tuvalu:{keywords:["flag","nation","country","banner"],char:'\u{1f1f9}\u{1f1fb}',fitzpatrick_scale:!1,category:"flags"},uganda:{keywords:["ug","flag","nation","country","banner"],char:'\u{1f1fa}\u{1f1ec}',fitzpatrick_scale:!1,category:"flags"},ukraine:{keywords:["ua","flag","nation","country","banner"],char:'\u{1f1fa}\u{1f1e6}',fitzpatrick_scale:!1,category:"flags"},united_arab_emirates:{keywords:["united","arab","emirates","flag","nation","country","banner"],char:'\u{1f1e6}\u{1f1ea}',fitzpatrick_scale:!1,category:"flags"},uk:{keywords:["united","kingdom","great","britain","northern","ireland","flag","nation","country","banner","british","UK","english","england","union jack"],char:'\u{1f1ec}\u{1f1e7}',fitzpatrick_scale:!1,category:"flags"},england:{keywords:["flag","english"],char:'\u{1f3f4}\u{e0067}\u{e0062}\u{e0065}\u{e006e}\u{e0067}\u{e007f}',fitzpatrick_scale:!1,category:"flags"},scotland:{keywords:["flag","scottish"],char:'\u{1f3f4}\u{e0067}\u{e0062}\u{e0073}\u{e0063}\u{e0074}\u{e007f}',fitzpatrick_scale:!1,category:"flags"},wales:{keywords:["flag","welsh"],char:'\u{1f3f4}\u{e0067}\u{e0062}\u{e0077}\u{e006c}\u{e0073}\u{e007f}',fitzpatrick_scale:!1,category:"flags"},us:{keywords:["united","states","america","flag","nation","country","banner"],char:'\u{1f1fa}\u{1f1f8}',fitzpatrick_scale:!1,category:"flags"},us_virgin_islands:{keywords:["virgin","islands","us","flag","nation","country","banner"],char:'\u{1f1fb}\u{1f1ee}',fitzpatrick_scale:!1,category:"flags"},uruguay:{keywords:["uy","flag","nation","country","banner"],char:'\u{1f1fa}\u{1f1fe}',fitzpatrick_scale:!1,category:"flags"},uzbekistan:{keywords:["uz","flag","nation","country","banner"],char:'\u{1f1fa}\u{1f1ff}',fitzpatrick_scale:!1,category:"flags"},vanuatu:{keywords:["vu","flag","nation","country","banner"],char:'\u{1f1fb}\u{1f1fa}',fitzpatrick_scale:!1,category:"flags"},vatican_city:{keywords:["vatican","city","flag","nation","country","banner"],char:'\u{1f1fb}\u{1f1e6}',fitzpatrick_scale:!1,category:"flags"},venezuela:{keywords:["ve","bolivarian","republic","flag","nation","country","banner"],char:'\u{1f1fb}\u{1f1ea}',fitzpatrick_scale:!1,category:"flags"},vietnam:{keywords:["viet","nam","flag","nation","country","banner"],char:'\u{1f1fb}\u{1f1f3}',fitzpatrick_scale:!1,category:"flags"},wallis_futuna:{keywords:["wallis","futuna","flag","nation","country","banner"],char:'\u{1f1fc}\u{1f1eb}',fitzpatrick_scale:!1,category:"flags"},western_sahara:{keywords:["western","sahara","flag","nation","country","banner"],char:'\u{1f1ea}\u{1f1ed}',fitzpatrick_scale:!1,category:"flags"},yemen:{keywords:["ye","flag","nation","country","banner"],char:'\u{1f1fe}\u{1f1ea}',fitzpatrick_scale:!1,category:"flags"},zambia:{keywords:["zm","flag","nation","country","banner"],char:'\u{1f1ff}\u{1f1f2}',fitzpatrick_scale:!1,category:"flags"},zimbabwe:{keywords:["zw","flag","nation","country","banner"],char:'\u{1f1ff}\u{1f1fc}',fitzpatrick_scale:!1,category:"flags"},united_nations:{keywords:["un","flag","banner"],char:'\u{1f1fa}\u{1f1f3}',fitzpatrick_scale:!1,category:"flags"},pirate_flag:{keywords:["skull","crossbones","flag","banner"],char:'\u{1f3f4}\u200d\u2620\ufe0f',fitzpatrick_scale:!1,category:"flags"}}); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/emoticons/js/emojis.js b/apps/web-antd/public/tinymce/plugins/emoticons/js/emojis.js new file mode 100644 index 0000000..88455e9 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/emoticons/js/emojis.js @@ -0,0 +1 @@ +window.tinymce.Resource.add("tinymce.plugins.emoticons",{grinning:{keywords:["face","smile","happy","joy",":D","grin"],char:"😀",fitzpatrick_scale:false,category:"people"},grimacing:{keywords:["face","grimace","teeth"],char:"😬",fitzpatrick_scale:false,category:"people"},grin:{keywords:["face","happy","smile","joy","kawaii"],char:"😁",fitzpatrick_scale:false,category:"people"},joy:{keywords:["face","cry","tears","weep","happy","happytears","haha"],char:"😂",fitzpatrick_scale:false,category:"people"},rofl:{keywords:["face","rolling","floor","laughing","lol","haha"],char:"🤣",fitzpatrick_scale:false,category:"people"},partying:{keywords:["face","celebration","woohoo"],char:"🥳",fitzpatrick_scale:false,category:"people"},smiley:{keywords:["face","happy","joy","haha",":D",":)","smile","funny"],char:"😃",fitzpatrick_scale:false,category:"people"},smile:{keywords:["face","happy","joy","funny","haha","laugh","like",":D",":)"],char:"😄",fitzpatrick_scale:false,category:"people"},sweat_smile:{keywords:["face","hot","happy","laugh","sweat","smile","relief"],char:"😅",fitzpatrick_scale:false,category:"people"},laughing:{keywords:["happy","joy","lol","satisfied","haha","face","glad","XD","laugh"],char:"😆",fitzpatrick_scale:false,category:"people"},innocent:{keywords:["face","angel","heaven","halo"],char:"😇",fitzpatrick_scale:false,category:"people"},wink:{keywords:["face","happy","mischievous","secret",";)","smile","eye"],char:"😉",fitzpatrick_scale:false,category:"people"},blush:{keywords:["face","smile","happy","flushed","crush","embarrassed","shy","joy"],char:"😊",fitzpatrick_scale:false,category:"people"},slightly_smiling_face:{keywords:["face","smile"],char:"🙂",fitzpatrick_scale:false,category:"people"},upside_down_face:{keywords:["face","flipped","silly","smile"],char:"🙃",fitzpatrick_scale:false,category:"people"},relaxed:{keywords:["face","blush","massage","happiness"],char:"☺️",fitzpatrick_scale:false,category:"people"},yum:{keywords:["happy","joy","tongue","smile","face","silly","yummy","nom","delicious","savouring"],char:"😋",fitzpatrick_scale:false,category:"people"},relieved:{keywords:["face","relaxed","phew","massage","happiness"],char:"😌",fitzpatrick_scale:false,category:"people"},heart_eyes:{keywords:["face","love","like","affection","valentines","infatuation","crush","heart"],char:"😍",fitzpatrick_scale:false,category:"people"},smiling_face_with_three_hearts:{keywords:["face","love","like","affection","valentines","infatuation","crush","hearts","adore"],char:"🥰",fitzpatrick_scale:false,category:"people"},kissing_heart:{keywords:["face","love","like","affection","valentines","infatuation","kiss"],char:"😘",fitzpatrick_scale:false,category:"people"},kissing:{keywords:["love","like","face","3","valentines","infatuation","kiss"],char:"😗",fitzpatrick_scale:false,category:"people"},kissing_smiling_eyes:{keywords:["face","affection","valentines","infatuation","kiss"],char:"😙",fitzpatrick_scale:false,category:"people"},kissing_closed_eyes:{keywords:["face","love","like","affection","valentines","infatuation","kiss"],char:"😚",fitzpatrick_scale:false,category:"people"},stuck_out_tongue_winking_eye:{keywords:["face","prank","childish","playful","mischievous","smile","wink","tongue"],char:"😜",fitzpatrick_scale:false,category:"people"},zany:{keywords:["face","goofy","crazy"],char:"🤪",fitzpatrick_scale:false,category:"people"},raised_eyebrow:{keywords:["face","distrust","scepticism","disapproval","disbelief","surprise"],char:"🤨",fitzpatrick_scale:false,category:"people"},monocle:{keywords:["face","stuffy","wealthy"],char:"🧐",fitzpatrick_scale:false,category:"people"},stuck_out_tongue_closed_eyes:{keywords:["face","prank","playful","mischievous","smile","tongue"],char:"😝",fitzpatrick_scale:false,category:"people"},stuck_out_tongue:{keywords:["face","prank","childish","playful","mischievous","smile","tongue"],char:"😛",fitzpatrick_scale:false,category:"people"},money_mouth_face:{keywords:["face","rich","dollar","money"],char:"🤑",fitzpatrick_scale:false,category:"people"},nerd_face:{keywords:["face","nerdy","geek","dork"],char:"🤓",fitzpatrick_scale:false,category:"people"},sunglasses:{keywords:["face","cool","smile","summer","beach","sunglass"],char:"😎",fitzpatrick_scale:false,category:"people"},star_struck:{keywords:["face","smile","starry","eyes","grinning"],char:"🤩",fitzpatrick_scale:false,category:"people"},clown_face:{keywords:["face"],char:"🤡",fitzpatrick_scale:false,category:"people"},cowboy_hat_face:{keywords:["face","cowgirl","hat"],char:"🤠",fitzpatrick_scale:false,category:"people"},hugs:{keywords:["face","smile","hug"],char:"🤗",fitzpatrick_scale:false,category:"people"},smirk:{keywords:["face","smile","mean","prank","smug","sarcasm"],char:"😏",fitzpatrick_scale:false,category:"people"},no_mouth:{keywords:["face","hellokitty"],char:"😶",fitzpatrick_scale:false,category:"people"},neutral_face:{keywords:["indifference","meh",":|","neutral"],char:"😐",fitzpatrick_scale:false,category:"people"},expressionless:{keywords:["face","indifferent","-_-","meh","deadpan"],char:"😑",fitzpatrick_scale:false,category:"people"},unamused:{keywords:["indifference","bored","straight face","serious","sarcasm","unimpressed","skeptical","dubious","side_eye"],char:"😒",fitzpatrick_scale:false,category:"people"},roll_eyes:{keywords:["face","eyeroll","frustrated"],char:"🙄",fitzpatrick_scale:false,category:"people"},thinking:{keywords:["face","hmmm","think","consider"],char:"🤔",fitzpatrick_scale:false,category:"people"},lying_face:{keywords:["face","lie","pinocchio"],char:"🤥",fitzpatrick_scale:false,category:"people"},hand_over_mouth:{keywords:["face","whoops","shock","surprise"],char:"🤭",fitzpatrick_scale:false,category:"people"},shushing:{keywords:["face","quiet","shhh"],char:"🤫",fitzpatrick_scale:false,category:"people"},symbols_over_mouth:{keywords:["face","swearing","cursing","cussing","profanity","expletive"],char:"🤬",fitzpatrick_scale:false,category:"people"},exploding_head:{keywords:["face","shocked","mind","blown"],char:"🤯",fitzpatrick_scale:false,category:"people"},flushed:{keywords:["face","blush","shy","flattered"],char:"😳",fitzpatrick_scale:false,category:"people"},disappointed:{keywords:["face","sad","upset","depressed",":("],char:"😞",fitzpatrick_scale:false,category:"people"},worried:{keywords:["face","concern","nervous",":("],char:"😟",fitzpatrick_scale:false,category:"people"},angry:{keywords:["mad","face","annoyed","frustrated"],char:"😠",fitzpatrick_scale:false,category:"people"},rage:{keywords:["angry","mad","hate","despise"],char:"😡",fitzpatrick_scale:false,category:"people"},pensive:{keywords:["face","sad","depressed","upset"],char:"😔",fitzpatrick_scale:false,category:"people"},confused:{keywords:["face","indifference","huh","weird","hmmm",":/"],char:"😕",fitzpatrick_scale:false,category:"people"},slightly_frowning_face:{keywords:["face","frowning","disappointed","sad","upset"],char:"🙁",fitzpatrick_scale:false,category:"people"},frowning_face:{keywords:["face","sad","upset","frown"],char:"☹",fitzpatrick_scale:false,category:"people"},persevere:{keywords:["face","sick","no","upset","oops"],char:"😣",fitzpatrick_scale:false,category:"people"},confounded:{keywords:["face","confused","sick","unwell","oops",":S"],char:"😖",fitzpatrick_scale:false,category:"people"},tired_face:{keywords:["sick","whine","upset","frustrated"],char:"😫",fitzpatrick_scale:false,category:"people"},weary:{keywords:["face","tired","sleepy","sad","frustrated","upset"],char:"😩",fitzpatrick_scale:false,category:"people"},pleading:{keywords:["face","begging","mercy"],char:"🥺",fitzpatrick_scale:false,category:"people"},triumph:{keywords:["face","gas","phew","proud","pride"],char:"😤",fitzpatrick_scale:false,category:"people"},open_mouth:{keywords:["face","surprise","impressed","wow","whoa",":O"],char:"😮",fitzpatrick_scale:false,category:"people"},scream:{keywords:["face","munch","scared","omg"],char:"😱",fitzpatrick_scale:false,category:"people"},fearful:{keywords:["face","scared","terrified","nervous","oops","huh"],char:"😨",fitzpatrick_scale:false,category:"people"},cold_sweat:{keywords:["face","nervous","sweat"],char:"😰",fitzpatrick_scale:false,category:"people"},hushed:{keywords:["face","woo","shh"],char:"😯",fitzpatrick_scale:false,category:"people"},frowning:{keywords:["face","aw","what"],char:"😦",fitzpatrick_scale:false,category:"people"},anguished:{keywords:["face","stunned","nervous"],char:"😧",fitzpatrick_scale:false,category:"people"},cry:{keywords:["face","tears","sad","depressed","upset",":'("],char:"😢",fitzpatrick_scale:false,category:"people"},disappointed_relieved:{keywords:["face","phew","sweat","nervous"],char:"😥",fitzpatrick_scale:false,category:"people"},drooling_face:{keywords:["face"],char:"🤤",fitzpatrick_scale:false,category:"people"},sleepy:{keywords:["face","tired","rest","nap"],char:"😪",fitzpatrick_scale:false,category:"people"},sweat:{keywords:["face","hot","sad","tired","exercise"],char:"😓",fitzpatrick_scale:false,category:"people"},hot:{keywords:["face","feverish","heat","red","sweating"],char:"🥵",fitzpatrick_scale:false,category:"people"},cold:{keywords:["face","blue","freezing","frozen","frostbite","icicles"],char:"🥶",fitzpatrick_scale:false,category:"people"},sob:{keywords:["face","cry","tears","sad","upset","depressed"],char:"😭",fitzpatrick_scale:false,category:"people"},dizzy_face:{keywords:["spent","unconscious","xox","dizzy"],char:"😵",fitzpatrick_scale:false,category:"people"},astonished:{keywords:["face","xox","surprised","poisoned"],char:"😲",fitzpatrick_scale:false,category:"people"},zipper_mouth_face:{keywords:["face","sealed","zipper","secret"],char:"🤐",fitzpatrick_scale:false,category:"people"},nauseated_face:{keywords:["face","vomit","gross","green","sick","throw up","ill"],char:"🤢",fitzpatrick_scale:false,category:"people"},sneezing_face:{keywords:["face","gesundheit","sneeze","sick","allergy"],char:"🤧",fitzpatrick_scale:false,category:"people"},vomiting:{keywords:["face","sick"],char:"🤮",fitzpatrick_scale:false,category:"people"},mask:{keywords:["face","sick","ill","disease"],char:"😷",fitzpatrick_scale:false,category:"people"},face_with_thermometer:{keywords:["sick","temperature","thermometer","cold","fever"],char:"🤒",fitzpatrick_scale:false,category:"people"},face_with_head_bandage:{keywords:["injured","clumsy","bandage","hurt"],char:"🤕",fitzpatrick_scale:false,category:"people"},woozy:{keywords:["face","dizzy","intoxicated","tipsy","wavy"],char:"🥴",fitzpatrick_scale:false,category:"people"},sleeping:{keywords:["face","tired","sleepy","night","zzz"],char:"😴",fitzpatrick_scale:false,category:"people"},zzz:{keywords:["sleepy","tired","dream"],char:"💤",fitzpatrick_scale:false,category:"people"},poop:{keywords:["hankey","shitface","fail","turd","shit"],char:"💩",fitzpatrick_scale:false,category:"people"},smiling_imp:{keywords:["devil","horns"],char:"😈",fitzpatrick_scale:false,category:"people"},imp:{keywords:["devil","angry","horns"],char:"👿",fitzpatrick_scale:false,category:"people"},japanese_ogre:{keywords:["monster","red","mask","halloween","scary","creepy","devil","demon","japanese","ogre"],char:"👹",fitzpatrick_scale:false,category:"people"},japanese_goblin:{keywords:["red","evil","mask","monster","scary","creepy","japanese","goblin"],char:"👺",fitzpatrick_scale:false,category:"people"},skull:{keywords:["dead","skeleton","creepy","death"],char:"💀",fitzpatrick_scale:false,category:"people"},ghost:{keywords:["halloween","spooky","scary"],char:"👻",fitzpatrick_scale:false,category:"people"},alien:{keywords:["UFO","paul","weird","outer_space"],char:"👽",fitzpatrick_scale:false,category:"people"},robot:{keywords:["computer","machine","bot"],char:"🤖",fitzpatrick_scale:false,category:"people"},smiley_cat:{keywords:["animal","cats","happy","smile"],char:"😺",fitzpatrick_scale:false,category:"people"},smile_cat:{keywords:["animal","cats","smile"],char:"😸",fitzpatrick_scale:false,category:"people"},joy_cat:{keywords:["animal","cats","haha","happy","tears"],char:"😹",fitzpatrick_scale:false,category:"people"},heart_eyes_cat:{keywords:["animal","love","like","affection","cats","valentines","heart"],char:"😻",fitzpatrick_scale:false,category:"people"},smirk_cat:{keywords:["animal","cats","smirk"],char:"😼",fitzpatrick_scale:false,category:"people"},kissing_cat:{keywords:["animal","cats","kiss"],char:"😽",fitzpatrick_scale:false,category:"people"},scream_cat:{keywords:["animal","cats","munch","scared","scream"],char:"🙀",fitzpatrick_scale:false,category:"people"},crying_cat_face:{keywords:["animal","tears","weep","sad","cats","upset","cry"],char:"😿",fitzpatrick_scale:false,category:"people"},pouting_cat:{keywords:["animal","cats"],char:"😾",fitzpatrick_scale:false,category:"people"},palms_up:{keywords:["hands","gesture","cupped","prayer"],char:"🤲",fitzpatrick_scale:true,category:"people"},raised_hands:{keywords:["gesture","hooray","yea","celebration","hands"],char:"🙌",fitzpatrick_scale:true,category:"people"},clap:{keywords:["hands","praise","applause","congrats","yay"],char:"👏",fitzpatrick_scale:true,category:"people"},wave:{keywords:["hands","gesture","goodbye","solong","farewell","hello","hi","palm"],char:"👋",fitzpatrick_scale:true,category:"people"},call_me_hand:{keywords:["hands","gesture"],char:"🤙",fitzpatrick_scale:true,category:"people"},"+1":{keywords:["thumbsup","yes","awesome","good","agree","accept","cool","hand","like"],char:"👍",fitzpatrick_scale:true,category:"people"},"-1":{keywords:["thumbsdown","no","dislike","hand"],char:"👎",fitzpatrick_scale:true,category:"people"},facepunch:{keywords:["angry","violence","fist","hit","attack","hand"],char:"👊",fitzpatrick_scale:true,category:"people"},fist:{keywords:["fingers","hand","grasp"],char:"✊",fitzpatrick_scale:true,category:"people"},fist_left:{keywords:["hand","fistbump"],char:"🤛",fitzpatrick_scale:true,category:"people"},fist_right:{keywords:["hand","fistbump"],char:"🤜",fitzpatrick_scale:true,category:"people"},v:{keywords:["fingers","ohyeah","hand","peace","victory","two"],char:"✌",fitzpatrick_scale:true,category:"people"},ok_hand:{keywords:["fingers","limbs","perfect","ok","okay"],char:"👌",fitzpatrick_scale:true,category:"people"},raised_hand:{keywords:["fingers","stop","highfive","palm","ban"],char:"✋",fitzpatrick_scale:true,category:"people"},raised_back_of_hand:{keywords:["fingers","raised","backhand"],char:"🤚",fitzpatrick_scale:true,category:"people"},open_hands:{keywords:["fingers","butterfly","hands","open"],char:"👐",fitzpatrick_scale:true,category:"people"},muscle:{keywords:["arm","flex","hand","summer","strong","biceps"],char:"💪",fitzpatrick_scale:true,category:"people"},pray:{keywords:["please","hope","wish","namaste","highfive"],char:"🙏",fitzpatrick_scale:true,category:"people"},foot:{keywords:["kick","stomp"],char:"🦶",fitzpatrick_scale:true,category:"people"},leg:{keywords:["kick","limb"],char:"🦵",fitzpatrick_scale:true,category:"people"},handshake:{keywords:["agreement","shake"],char:"🤝",fitzpatrick_scale:false,category:"people"},point_up:{keywords:["hand","fingers","direction","up"],char:"☝",fitzpatrick_scale:true,category:"people"},point_up_2:{keywords:["fingers","hand","direction","up"],char:"👆",fitzpatrick_scale:true,category:"people"},point_down:{keywords:["fingers","hand","direction","down"],char:"👇",fitzpatrick_scale:true,category:"people"},point_left:{keywords:["direction","fingers","hand","left"],char:"👈",fitzpatrick_scale:true,category:"people"},point_right:{keywords:["fingers","hand","direction","right"],char:"👉",fitzpatrick_scale:true,category:"people"},fu:{keywords:["hand","fingers","rude","middle","flipping"],char:"🖕",fitzpatrick_scale:true,category:"people"},raised_hand_with_fingers_splayed:{keywords:["hand","fingers","palm"],char:"🖐",fitzpatrick_scale:true,category:"people"},love_you:{keywords:["hand","fingers","gesture"],char:"🤟",fitzpatrick_scale:true,category:"people"},metal:{keywords:["hand","fingers","evil_eye","sign_of_horns","rock_on"],char:"🤘",fitzpatrick_scale:true,category:"people"},crossed_fingers:{keywords:["good","lucky"],char:"🤞",fitzpatrick_scale:true,category:"people"},vulcan_salute:{keywords:["hand","fingers","spock","star trek"],char:"🖖",fitzpatrick_scale:true,category:"people"},writing_hand:{keywords:["lower_left_ballpoint_pen","stationery","write","compose"],char:"✍",fitzpatrick_scale:true,category:"people"},selfie:{keywords:["camera","phone"],char:"🤳",fitzpatrick_scale:true,category:"people"},nail_care:{keywords:["beauty","manicure","finger","fashion","nail"],char:"💅",fitzpatrick_scale:true,category:"people"},lips:{keywords:["mouth","kiss"],char:"👄",fitzpatrick_scale:false,category:"people"},tooth:{keywords:["teeth","dentist"],char:"🦷",fitzpatrick_scale:false,category:"people"},tongue:{keywords:["mouth","playful"],char:"👅",fitzpatrick_scale:false,category:"people"},ear:{keywords:["face","hear","sound","listen"],char:"👂",fitzpatrick_scale:true,category:"people"},nose:{keywords:["smell","sniff"],char:"👃",fitzpatrick_scale:true,category:"people"},eye:{keywords:["face","look","see","watch","stare"],char:"👁",fitzpatrick_scale:false,category:"people"},eyes:{keywords:["look","watch","stalk","peek","see"],char:"👀",fitzpatrick_scale:false,category:"people"},brain:{keywords:["smart","intelligent"],char:"🧠",fitzpatrick_scale:false,category:"people"},bust_in_silhouette:{keywords:["user","person","human"],char:"👤",fitzpatrick_scale:false,category:"people"},busts_in_silhouette:{keywords:["user","person","human","group","team"],char:"👥",fitzpatrick_scale:false,category:"people"},speaking_head:{keywords:["user","person","human","sing","say","talk"],char:"🗣",fitzpatrick_scale:false,category:"people"},baby:{keywords:["child","boy","girl","toddler"],char:"👶",fitzpatrick_scale:true,category:"people"},child:{keywords:["gender-neutral","young"],char:"🧒",fitzpatrick_scale:true,category:"people"},boy:{keywords:["man","male","guy","teenager"],char:"👦",fitzpatrick_scale:true,category:"people"},girl:{keywords:["female","woman","teenager"],char:"👧",fitzpatrick_scale:true,category:"people"},adult:{keywords:["gender-neutral","person"],char:"🧑",fitzpatrick_scale:true,category:"people"},man:{keywords:["mustache","father","dad","guy","classy","sir","moustache"],char:"👨",fitzpatrick_scale:true,category:"people"},woman:{keywords:["female","girls","lady"],char:"👩",fitzpatrick_scale:true,category:"people"},blonde_woman:{keywords:["woman","female","girl","blonde","person"],char:"👱‍♀️",fitzpatrick_scale:true,category:"people"},blonde_man:{keywords:["man","male","boy","blonde","guy","person"],char:"👱",fitzpatrick_scale:true,category:"people"},bearded_person:{keywords:["person","bewhiskered"],char:"🧔",fitzpatrick_scale:true,category:"people"},older_adult:{keywords:["human","elder","senior","gender-neutral"],char:"🧓",fitzpatrick_scale:true,category:"people"},older_man:{keywords:["human","male","men","old","elder","senior"],char:"👴",fitzpatrick_scale:true,category:"people"},older_woman:{keywords:["human","female","women","lady","old","elder","senior"],char:"👵",fitzpatrick_scale:true,category:"people"},man_with_gua_pi_mao:{keywords:["male","boy","chinese"],char:"👲",fitzpatrick_scale:true,category:"people"},woman_with_headscarf:{keywords:["female","hijab","mantilla","tichel"],char:"🧕",fitzpatrick_scale:true,category:"people"},woman_with_turban:{keywords:["female","indian","hinduism","arabs","woman"],char:"👳‍♀️",fitzpatrick_scale:true,category:"people"},man_with_turban:{keywords:["male","indian","hinduism","arabs"],char:"👳",fitzpatrick_scale:true,category:"people"},policewoman:{keywords:["woman","police","law","legal","enforcement","arrest","911","female"],char:"👮‍♀️",fitzpatrick_scale:true,category:"people"},policeman:{keywords:["man","police","law","legal","enforcement","arrest","911"],char:"👮",fitzpatrick_scale:true,category:"people"},construction_worker_woman:{keywords:["female","human","wip","build","construction","worker","labor","woman"],char:"👷‍♀️",fitzpatrick_scale:true,category:"people"},construction_worker_man:{keywords:["male","human","wip","guy","build","construction","worker","labor"],char:"👷",fitzpatrick_scale:true,category:"people"},guardswoman:{keywords:["uk","gb","british","female","royal","woman"],char:"💂‍♀️",fitzpatrick_scale:true,category:"people"},guardsman:{keywords:["uk","gb","british","male","guy","royal"],char:"💂",fitzpatrick_scale:true,category:"people"},female_detective:{keywords:["human","spy","detective","female","woman"],char:"🕵️‍♀️",fitzpatrick_scale:true,category:"people"},male_detective:{keywords:["human","spy","detective"],char:"🕵",fitzpatrick_scale:true,category:"people"},woman_health_worker:{keywords:["doctor","nurse","therapist","healthcare","woman","human"],char:"👩‍⚕️",fitzpatrick_scale:true,category:"people"},man_health_worker:{keywords:["doctor","nurse","therapist","healthcare","man","human"],char:"👨‍⚕️",fitzpatrick_scale:true,category:"people"},woman_farmer:{keywords:["rancher","gardener","woman","human"],char:"👩‍🌾",fitzpatrick_scale:true,category:"people"},man_farmer:{keywords:["rancher","gardener","man","human"],char:"👨‍🌾",fitzpatrick_scale:true,category:"people"},woman_cook:{keywords:["chef","woman","human"],char:"👩‍🍳",fitzpatrick_scale:true,category:"people"},man_cook:{keywords:["chef","man","human"],char:"👨‍🍳",fitzpatrick_scale:true,category:"people"},woman_student:{keywords:["graduate","woman","human"],char:"👩‍🎓",fitzpatrick_scale:true,category:"people"},man_student:{keywords:["graduate","man","human"],char:"👨‍🎓",fitzpatrick_scale:true,category:"people"},woman_singer:{keywords:["rockstar","entertainer","woman","human"],char:"👩‍🎤",fitzpatrick_scale:true,category:"people"},man_singer:{keywords:["rockstar","entertainer","man","human"],char:"👨‍🎤",fitzpatrick_scale:true,category:"people"},woman_teacher:{keywords:["instructor","professor","woman","human"],char:"👩‍🏫",fitzpatrick_scale:true,category:"people"},man_teacher:{keywords:["instructor","professor","man","human"],char:"👨‍🏫",fitzpatrick_scale:true,category:"people"},woman_factory_worker:{keywords:["assembly","industrial","woman","human"],char:"👩‍🏭",fitzpatrick_scale:true,category:"people"},man_factory_worker:{keywords:["assembly","industrial","man","human"],char:"👨‍🏭",fitzpatrick_scale:true,category:"people"},woman_technologist:{keywords:["coder","developer","engineer","programmer","software","woman","human","laptop","computer"],char:"👩‍💻",fitzpatrick_scale:true,category:"people"},man_technologist:{keywords:["coder","developer","engineer","programmer","software","man","human","laptop","computer"],char:"👨‍💻",fitzpatrick_scale:true,category:"people"},woman_office_worker:{keywords:["business","manager","woman","human"],char:"👩‍💼",fitzpatrick_scale:true,category:"people"},man_office_worker:{keywords:["business","manager","man","human"],char:"👨‍💼",fitzpatrick_scale:true,category:"people"},woman_mechanic:{keywords:["plumber","woman","human","wrench"],char:"👩‍🔧",fitzpatrick_scale:true,category:"people"},man_mechanic:{keywords:["plumber","man","human","wrench"],char:"👨‍🔧",fitzpatrick_scale:true,category:"people"},woman_scientist:{keywords:["biologist","chemist","engineer","physicist","woman","human"],char:"👩‍🔬",fitzpatrick_scale:true,category:"people"},man_scientist:{keywords:["biologist","chemist","engineer","physicist","man","human"],char:"👨‍🔬",fitzpatrick_scale:true,category:"people"},woman_artist:{keywords:["painter","woman","human"],char:"👩‍🎨",fitzpatrick_scale:true,category:"people"},man_artist:{keywords:["painter","man","human"],char:"👨‍🎨",fitzpatrick_scale:true,category:"people"},woman_firefighter:{keywords:["fireman","woman","human"],char:"👩‍🚒",fitzpatrick_scale:true,category:"people"},man_firefighter:{keywords:["fireman","man","human"],char:"👨‍🚒",fitzpatrick_scale:true,category:"people"},woman_pilot:{keywords:["aviator","plane","woman","human"],char:"👩‍✈️",fitzpatrick_scale:true,category:"people"},man_pilot:{keywords:["aviator","plane","man","human"],char:"👨‍✈️",fitzpatrick_scale:true,category:"people"},woman_astronaut:{keywords:["space","rocket","woman","human"],char:"👩‍🚀",fitzpatrick_scale:true,category:"people"},man_astronaut:{keywords:["space","rocket","man","human"],char:"👨‍🚀",fitzpatrick_scale:true,category:"people"},woman_judge:{keywords:["justice","court","woman","human"],char:"👩‍⚖️",fitzpatrick_scale:true,category:"people"},man_judge:{keywords:["justice","court","man","human"],char:"👨‍⚖️",fitzpatrick_scale:true,category:"people"},woman_superhero:{keywords:["woman","female","good","heroine","superpowers"],char:"🦸‍♀️",fitzpatrick_scale:true,category:"people"},man_superhero:{keywords:["man","male","good","hero","superpowers"],char:"🦸‍♂️",fitzpatrick_scale:true,category:"people"},woman_supervillain:{keywords:["woman","female","evil","bad","criminal","heroine","superpowers"],char:"🦹‍♀️",fitzpatrick_scale:true,category:"people"},man_supervillain:{keywords:["man","male","evil","bad","criminal","hero","superpowers"],char:"🦹‍♂️",fitzpatrick_scale:true,category:"people"},mrs_claus:{keywords:["woman","female","xmas","mother christmas"],char:"🤶",fitzpatrick_scale:true,category:"people"},santa:{keywords:["festival","man","male","xmas","father christmas"],char:"🎅",fitzpatrick_scale:true,category:"people"},sorceress:{keywords:["woman","female","mage","witch"],char:"🧙‍♀️",fitzpatrick_scale:true,category:"people"},wizard:{keywords:["man","male","mage","sorcerer"],char:"🧙‍♂️",fitzpatrick_scale:true,category:"people"},woman_elf:{keywords:["woman","female"],char:"🧝‍♀️",fitzpatrick_scale:true,category:"people"},man_elf:{keywords:["man","male"],char:"🧝‍♂️",fitzpatrick_scale:true,category:"people"},woman_vampire:{keywords:["woman","female"],char:"🧛‍♀️",fitzpatrick_scale:true,category:"people"},man_vampire:{keywords:["man","male","dracula"],char:"🧛‍♂️",fitzpatrick_scale:true,category:"people"},woman_zombie:{keywords:["woman","female","undead","walking dead"],char:"🧟‍♀️",fitzpatrick_scale:false,category:"people"},man_zombie:{keywords:["man","male","dracula","undead","walking dead"],char:"🧟‍♂️",fitzpatrick_scale:false,category:"people"},woman_genie:{keywords:["woman","female"],char:"🧞‍♀️",fitzpatrick_scale:false,category:"people"},man_genie:{keywords:["man","male"],char:"🧞‍♂️",fitzpatrick_scale:false,category:"people"},mermaid:{keywords:["woman","female","merwoman","ariel"],char:"🧜‍♀️",fitzpatrick_scale:true,category:"people"},merman:{keywords:["man","male","triton"],char:"🧜‍♂️",fitzpatrick_scale:true,category:"people"},woman_fairy:{keywords:["woman","female"],char:"🧚‍♀️",fitzpatrick_scale:true,category:"people"},man_fairy:{keywords:["man","male"],char:"🧚‍♂️",fitzpatrick_scale:true,category:"people"},angel:{keywords:["heaven","wings","halo"],char:"👼",fitzpatrick_scale:true,category:"people"},pregnant_woman:{keywords:["baby"],char:"🤰",fitzpatrick_scale:true,category:"people"},breastfeeding:{keywords:["nursing","baby"],char:"🤱",fitzpatrick_scale:true,category:"people"},princess:{keywords:["girl","woman","female","blond","crown","royal","queen"],char:"👸",fitzpatrick_scale:true,category:"people"},prince:{keywords:["boy","man","male","crown","royal","king"],char:"🤴",fitzpatrick_scale:true,category:"people"},bride_with_veil:{keywords:["couple","marriage","wedding","woman","bride"],char:"👰",fitzpatrick_scale:true,category:"people"},man_in_tuxedo:{keywords:["couple","marriage","wedding","groom"],char:"🤵",fitzpatrick_scale:true,category:"people"},running_woman:{keywords:["woman","walking","exercise","race","running","female"],char:"🏃‍♀️",fitzpatrick_scale:true,category:"people"},running_man:{keywords:["man","walking","exercise","race","running"],char:"🏃",fitzpatrick_scale:true,category:"people"},walking_woman:{keywords:["human","feet","steps","woman","female"],char:"🚶‍♀️",fitzpatrick_scale:true,category:"people"},walking_man:{keywords:["human","feet","steps"],char:"🚶",fitzpatrick_scale:true,category:"people"},dancer:{keywords:["female","girl","woman","fun"],char:"💃",fitzpatrick_scale:true,category:"people"},man_dancing:{keywords:["male","boy","fun","dancer"],char:"🕺",fitzpatrick_scale:true,category:"people"},dancing_women:{keywords:["female","bunny","women","girls"],char:"👯",fitzpatrick_scale:false,category:"people"},dancing_men:{keywords:["male","bunny","men","boys"],char:"👯‍♂️",fitzpatrick_scale:false,category:"people"},couple:{keywords:["pair","people","human","love","date","dating","like","affection","valentines","marriage"],char:"👫",fitzpatrick_scale:false,category:"people"},two_men_holding_hands:{keywords:["pair","couple","love","like","bromance","friendship","people","human"],char:"👬",fitzpatrick_scale:false,category:"people"},two_women_holding_hands:{keywords:["pair","friendship","couple","love","like","female","people","human"],char:"👭",fitzpatrick_scale:false,category:"people"},bowing_woman:{keywords:["woman","female","girl"],char:"🙇‍♀️",fitzpatrick_scale:true,category:"people"},bowing_man:{keywords:["man","male","boy"],char:"🙇",fitzpatrick_scale:true,category:"people"},man_facepalming:{keywords:["man","male","boy","disbelief"],char:"🤦‍♂️",fitzpatrick_scale:true,category:"people"},woman_facepalming:{keywords:["woman","female","girl","disbelief"],char:"🤦‍♀️",fitzpatrick_scale:true,category:"people"},woman_shrugging:{keywords:["woman","female","girl","confused","indifferent","doubt"],char:"🤷",fitzpatrick_scale:true,category:"people"},man_shrugging:{keywords:["man","male","boy","confused","indifferent","doubt"],char:"🤷‍♂️",fitzpatrick_scale:true,category:"people"},tipping_hand_woman:{keywords:["female","girl","woman","human","information"],char:"💁",fitzpatrick_scale:true,category:"people"},tipping_hand_man:{keywords:["male","boy","man","human","information"],char:"💁‍♂️",fitzpatrick_scale:true,category:"people"},no_good_woman:{keywords:["female","girl","woman","nope"],char:"🙅",fitzpatrick_scale:true,category:"people"},no_good_man:{keywords:["male","boy","man","nope"],char:"🙅‍♂️",fitzpatrick_scale:true,category:"people"},ok_woman:{keywords:["women","girl","female","pink","human","woman"],char:"🙆",fitzpatrick_scale:true,category:"people"},ok_man:{keywords:["men","boy","male","blue","human","man"],char:"🙆‍♂️",fitzpatrick_scale:true,category:"people"},raising_hand_woman:{keywords:["female","girl","woman"],char:"🙋",fitzpatrick_scale:true,category:"people"},raising_hand_man:{keywords:["male","boy","man"],char:"🙋‍♂️",fitzpatrick_scale:true,category:"people"},pouting_woman:{keywords:["female","girl","woman"],char:"🙎",fitzpatrick_scale:true,category:"people"},pouting_man:{keywords:["male","boy","man"],char:"🙎‍♂️",fitzpatrick_scale:true,category:"people"},frowning_woman:{keywords:["female","girl","woman","sad","depressed","discouraged","unhappy"],char:"🙍",fitzpatrick_scale:true,category:"people"},frowning_man:{keywords:["male","boy","man","sad","depressed","discouraged","unhappy"],char:"🙍‍♂️",fitzpatrick_scale:true,category:"people"},haircut_woman:{keywords:["female","girl","woman"],char:"💇",fitzpatrick_scale:true,category:"people"},haircut_man:{keywords:["male","boy","man"],char:"💇‍♂️",fitzpatrick_scale:true,category:"people"},massage_woman:{keywords:["female","girl","woman","head"],char:"💆",fitzpatrick_scale:true,category:"people"},massage_man:{keywords:["male","boy","man","head"],char:"💆‍♂️",fitzpatrick_scale:true,category:"people"},woman_in_steamy_room:{keywords:["female","woman","spa","steamroom","sauna"],char:"🧖‍♀️",fitzpatrick_scale:true,category:"people"},man_in_steamy_room:{keywords:["male","man","spa","steamroom","sauna"],char:"🧖‍♂️",fitzpatrick_scale:true,category:"people"},couple_with_heart_woman_man:{keywords:["pair","love","like","affection","human","dating","valentines","marriage"],char:"💑",fitzpatrick_scale:false,category:"people"},couple_with_heart_woman_woman:{keywords:["pair","love","like","affection","human","dating","valentines","marriage"],char:"👩‍❤️‍👩",fitzpatrick_scale:false,category:"people"},couple_with_heart_man_man:{keywords:["pair","love","like","affection","human","dating","valentines","marriage"],char:"👨‍❤️‍👨",fitzpatrick_scale:false,category:"people"},couplekiss_man_woman:{keywords:["pair","valentines","love","like","dating","marriage"],char:"💏",fitzpatrick_scale:false,category:"people"},couplekiss_woman_woman:{keywords:["pair","valentines","love","like","dating","marriage"],char:"👩‍❤️‍💋‍👩",fitzpatrick_scale:false,category:"people"},couplekiss_man_man:{keywords:["pair","valentines","love","like","dating","marriage"],char:"👨‍❤️‍💋‍👨",fitzpatrick_scale:false,category:"people"},family_man_woman_boy:{keywords:["home","parents","child","mom","dad","father","mother","people","human"],char:"👪",fitzpatrick_scale:false,category:"people"},family_man_woman_girl:{keywords:["home","parents","people","human","child"],char:"👨‍👩‍👧",fitzpatrick_scale:false,category:"people"},family_man_woman_girl_boy:{keywords:["home","parents","people","human","children"],char:"👨‍👩‍👧‍👦",fitzpatrick_scale:false,category:"people"},family_man_woman_boy_boy:{keywords:["home","parents","people","human","children"],char:"👨‍👩‍👦‍👦",fitzpatrick_scale:false,category:"people"},family_man_woman_girl_girl:{keywords:["home","parents","people","human","children"],char:"👨‍👩‍👧‍👧",fitzpatrick_scale:false,category:"people"},family_woman_woman_boy:{keywords:["home","parents","people","human","children"],char:"👩‍👩‍👦",fitzpatrick_scale:false,category:"people"},family_woman_woman_girl:{keywords:["home","parents","people","human","children"],char:"👩‍👩‍👧",fitzpatrick_scale:false,category:"people"},family_woman_woman_girl_boy:{keywords:["home","parents","people","human","children"],char:"👩‍👩‍👧‍👦",fitzpatrick_scale:false,category:"people"},family_woman_woman_boy_boy:{keywords:["home","parents","people","human","children"],char:"👩‍👩‍👦‍👦",fitzpatrick_scale:false,category:"people"},family_woman_woman_girl_girl:{keywords:["home","parents","people","human","children"],char:"👩‍👩‍👧‍👧",fitzpatrick_scale:false,category:"people"},family_man_man_boy:{keywords:["home","parents","people","human","children"],char:"👨‍👨‍👦",fitzpatrick_scale:false,category:"people"},family_man_man_girl:{keywords:["home","parents","people","human","children"],char:"👨‍👨‍👧",fitzpatrick_scale:false,category:"people"},family_man_man_girl_boy:{keywords:["home","parents","people","human","children"],char:"👨‍👨‍👧‍👦",fitzpatrick_scale:false,category:"people"},family_man_man_boy_boy:{keywords:["home","parents","people","human","children"],char:"👨‍👨‍👦‍👦",fitzpatrick_scale:false,category:"people"},family_man_man_girl_girl:{keywords:["home","parents","people","human","children"],char:"👨‍👨‍👧‍👧",fitzpatrick_scale:false,category:"people"},family_woman_boy:{keywords:["home","parent","people","human","child"],char:"👩‍👦",fitzpatrick_scale:false,category:"people"},family_woman_girl:{keywords:["home","parent","people","human","child"],char:"👩‍👧",fitzpatrick_scale:false,category:"people"},family_woman_girl_boy:{keywords:["home","parent","people","human","children"],char:"👩‍👧‍👦",fitzpatrick_scale:false,category:"people"},family_woman_boy_boy:{keywords:["home","parent","people","human","children"],char:"👩‍👦‍👦",fitzpatrick_scale:false,category:"people"},family_woman_girl_girl:{keywords:["home","parent","people","human","children"],char:"👩‍👧‍👧",fitzpatrick_scale:false,category:"people"},family_man_boy:{keywords:["home","parent","people","human","child"],char:"👨‍👦",fitzpatrick_scale:false,category:"people"},family_man_girl:{keywords:["home","parent","people","human","child"],char:"👨‍👧",fitzpatrick_scale:false,category:"people"},family_man_girl_boy:{keywords:["home","parent","people","human","children"],char:"👨‍👧‍👦",fitzpatrick_scale:false,category:"people"},family_man_boy_boy:{keywords:["home","parent","people","human","children"],char:"👨‍👦‍👦",fitzpatrick_scale:false,category:"people"},family_man_girl_girl:{keywords:["home","parent","people","human","children"],char:"👨‍👧‍👧",fitzpatrick_scale:false,category:"people"},yarn:{keywords:["ball","crochet","knit"],char:"🧶",fitzpatrick_scale:false,category:"people"},thread:{keywords:["needle","sewing","spool","string"],char:"🧵",fitzpatrick_scale:false,category:"people"},coat:{keywords:["jacket"],char:"🧥",fitzpatrick_scale:false,category:"people"},labcoat:{keywords:["doctor","experiment","scientist","chemist"],char:"🥼",fitzpatrick_scale:false,category:"people"},womans_clothes:{keywords:["fashion","shopping_bags","female"],char:"👚",fitzpatrick_scale:false,category:"people"},tshirt:{keywords:["fashion","cloth","casual","shirt","tee"],char:"👕",fitzpatrick_scale:false,category:"people"},jeans:{keywords:["fashion","shopping"],char:"👖",fitzpatrick_scale:false,category:"people"},necktie:{keywords:["shirt","suitup","formal","fashion","cloth","business"],char:"👔",fitzpatrick_scale:false,category:"people"},dress:{keywords:["clothes","fashion","shopping"],char:"👗",fitzpatrick_scale:false,category:"people"},bikini:{keywords:["swimming","female","woman","girl","fashion","beach","summer"],char:"👙",fitzpatrick_scale:false,category:"people"},kimono:{keywords:["dress","fashion","women","female","japanese"],char:"👘",fitzpatrick_scale:false,category:"people"},lipstick:{keywords:["female","girl","fashion","woman"],char:"💄",fitzpatrick_scale:false,category:"people"},kiss:{keywords:["face","lips","love","like","affection","valentines"],char:"💋",fitzpatrick_scale:false,category:"people"},footprints:{keywords:["feet","tracking","walking","beach"],char:"👣",fitzpatrick_scale:false,category:"people"},flat_shoe:{keywords:["ballet","slip-on","slipper"],char:"🥿",fitzpatrick_scale:false,category:"people"},high_heel:{keywords:["fashion","shoes","female","pumps","stiletto"],char:"👠",fitzpatrick_scale:false,category:"people"},sandal:{keywords:["shoes","fashion","flip flops"],char:"👡",fitzpatrick_scale:false,category:"people"},boot:{keywords:["shoes","fashion"],char:"👢",fitzpatrick_scale:false,category:"people"},mans_shoe:{keywords:["fashion","male"],char:"👞",fitzpatrick_scale:false,category:"people"},athletic_shoe:{keywords:["shoes","sports","sneakers"],char:"👟",fitzpatrick_scale:false,category:"people"},hiking_boot:{keywords:["backpacking","camping","hiking"],char:"🥾",fitzpatrick_scale:false,category:"people"},socks:{keywords:["stockings","clothes"],char:"🧦",fitzpatrick_scale:false,category:"people"},gloves:{keywords:["hands","winter","clothes"],char:"🧤",fitzpatrick_scale:false,category:"people"},scarf:{keywords:["neck","winter","clothes"],char:"🧣",fitzpatrick_scale:false,category:"people"},womans_hat:{keywords:["fashion","accessories","female","lady","spring"],char:"👒",fitzpatrick_scale:false,category:"people"},tophat:{keywords:["magic","gentleman","classy","circus"],char:"🎩",fitzpatrick_scale:false,category:"people"},billed_hat:{keywords:["cap","baseball"],char:"🧢",fitzpatrick_scale:false,category:"people"},rescue_worker_helmet:{keywords:["construction","build"],char:"⛑",fitzpatrick_scale:false,category:"people"},mortar_board:{keywords:["school","college","degree","university","graduation","cap","hat","legal","learn","education"],char:"🎓",fitzpatrick_scale:false,category:"people"},crown:{keywords:["king","kod","leader","royalty","lord"],char:"👑",fitzpatrick_scale:false,category:"people"},school_satchel:{keywords:["student","education","bag","backpack"],char:"🎒",fitzpatrick_scale:false,category:"people"},luggage:{keywords:["packing","travel"],char:"🧳",fitzpatrick_scale:false,category:"people"},pouch:{keywords:["bag","accessories","shopping"],char:"👝",fitzpatrick_scale:false,category:"people"},purse:{keywords:["fashion","accessories","money","sales","shopping"],char:"👛",fitzpatrick_scale:false,category:"people"},handbag:{keywords:["fashion","accessory","accessories","shopping"],char:"👜",fitzpatrick_scale:false,category:"people"},briefcase:{keywords:["business","documents","work","law","legal","job","career"],char:"💼",fitzpatrick_scale:false,category:"people"},eyeglasses:{keywords:["fashion","accessories","eyesight","nerdy","dork","geek"],char:"👓",fitzpatrick_scale:false,category:"people"},dark_sunglasses:{keywords:["face","cool","accessories"],char:"🕶",fitzpatrick_scale:false,category:"people"},goggles:{keywords:["eyes","protection","safety"],char:"🥽",fitzpatrick_scale:false,category:"people"},ring:{keywords:["wedding","propose","marriage","valentines","diamond","fashion","jewelry","gem","engagement"],char:"💍",fitzpatrick_scale:false,category:"people"},closed_umbrella:{keywords:["weather","rain","drizzle"],char:"🌂",fitzpatrick_scale:false,category:"people"},dog:{keywords:["animal","friend","nature","woof","puppy","pet","faithful"],char:"🐶",fitzpatrick_scale:false,category:"animals_and_nature"},cat:{keywords:["animal","meow","nature","pet","kitten"],char:"🐱",fitzpatrick_scale:false,category:"animals_and_nature"},mouse:{keywords:["animal","nature","cheese_wedge","rodent"],char:"🐭",fitzpatrick_scale:false,category:"animals_and_nature"},hamster:{keywords:["animal","nature"],char:"🐹",fitzpatrick_scale:false,category:"animals_and_nature"},rabbit:{keywords:["animal","nature","pet","spring","magic","bunny"],char:"🐰",fitzpatrick_scale:false,category:"animals_and_nature"},fox_face:{keywords:["animal","nature","face"],char:"🦊",fitzpatrick_scale:false,category:"animals_and_nature"},bear:{keywords:["animal","nature","wild"],char:"🐻",fitzpatrick_scale:false,category:"animals_and_nature"},panda_face:{keywords:["animal","nature","panda"],char:"🐼",fitzpatrick_scale:false,category:"animals_and_nature"},koala:{keywords:["animal","nature"],char:"🐨",fitzpatrick_scale:false,category:"animals_and_nature"},tiger:{keywords:["animal","cat","danger","wild","nature","roar"],char:"🐯",fitzpatrick_scale:false,category:"animals_and_nature"},lion:{keywords:["animal","nature"],char:"🦁",fitzpatrick_scale:false,category:"animals_and_nature"},cow:{keywords:["beef","ox","animal","nature","moo","milk"],char:"🐮",fitzpatrick_scale:false,category:"animals_and_nature"},pig:{keywords:["animal","oink","nature"],char:"🐷",fitzpatrick_scale:false,category:"animals_and_nature"},pig_nose:{keywords:["animal","oink"],char:"🐽",fitzpatrick_scale:false,category:"animals_and_nature"},frog:{keywords:["animal","nature","croak","toad"],char:"🐸",fitzpatrick_scale:false,category:"animals_and_nature"},squid:{keywords:["animal","nature","ocean","sea"],char:"🦑",fitzpatrick_scale:false,category:"animals_and_nature"},octopus:{keywords:["animal","creature","ocean","sea","nature","beach"],char:"🐙",fitzpatrick_scale:false,category:"animals_and_nature"},shrimp:{keywords:["animal","ocean","nature","seafood"],char:"🦐",fitzpatrick_scale:false,category:"animals_and_nature"},monkey_face:{keywords:["animal","nature","circus"],char:"🐵",fitzpatrick_scale:false,category:"animals_and_nature"},gorilla:{keywords:["animal","nature","circus"],char:"🦍",fitzpatrick_scale:false,category:"animals_and_nature"},see_no_evil:{keywords:["monkey","animal","nature","haha"],char:"🙈",fitzpatrick_scale:false,category:"animals_and_nature"},hear_no_evil:{keywords:["animal","monkey","nature"],char:"🙉",fitzpatrick_scale:false,category:"animals_and_nature"},speak_no_evil:{keywords:["monkey","animal","nature","omg"],char:"🙊",fitzpatrick_scale:false,category:"animals_and_nature"},monkey:{keywords:["animal","nature","banana","circus"],char:"🐒",fitzpatrick_scale:false,category:"animals_and_nature"},chicken:{keywords:["animal","cluck","nature","bird"],char:"🐔",fitzpatrick_scale:false,category:"animals_and_nature"},penguin:{keywords:["animal","nature"],char:"🐧",fitzpatrick_scale:false,category:"animals_and_nature"},bird:{keywords:["animal","nature","fly","tweet","spring"],char:"🐦",fitzpatrick_scale:false,category:"animals_and_nature"},baby_chick:{keywords:["animal","chicken","bird"],char:"🐤",fitzpatrick_scale:false,category:"animals_and_nature"},hatching_chick:{keywords:["animal","chicken","egg","born","baby","bird"],char:"🐣",fitzpatrick_scale:false,category:"animals_and_nature"},hatched_chick:{keywords:["animal","chicken","baby","bird"],char:"🐥",fitzpatrick_scale:false,category:"animals_and_nature"},duck:{keywords:["animal","nature","bird","mallard"],char:"🦆",fitzpatrick_scale:false,category:"animals_and_nature"},eagle:{keywords:["animal","nature","bird"],char:"🦅",fitzpatrick_scale:false,category:"animals_and_nature"},owl:{keywords:["animal","nature","bird","hoot"],char:"🦉",fitzpatrick_scale:false,category:"animals_and_nature"},bat:{keywords:["animal","nature","blind","vampire"],char:"🦇",fitzpatrick_scale:false,category:"animals_and_nature"},wolf:{keywords:["animal","nature","wild"],char:"🐺",fitzpatrick_scale:false,category:"animals_and_nature"},boar:{keywords:["animal","nature"],char:"🐗",fitzpatrick_scale:false,category:"animals_and_nature"},horse:{keywords:["animal","brown","nature"],char:"🐴",fitzpatrick_scale:false,category:"animals_and_nature"},unicorn:{keywords:["animal","nature","mystical"],char:"🦄",fitzpatrick_scale:false,category:"animals_and_nature"},honeybee:{keywords:["animal","insect","nature","bug","spring","honey"],char:"🐝",fitzpatrick_scale:false,category:"animals_and_nature"},bug:{keywords:["animal","insect","nature","worm"],char:"🐛",fitzpatrick_scale:false,category:"animals_and_nature"},butterfly:{keywords:["animal","insect","nature","caterpillar"],char:"🦋",fitzpatrick_scale:false,category:"animals_and_nature"},snail:{keywords:["slow","animal","shell"],char:"🐌",fitzpatrick_scale:false,category:"animals_and_nature"},beetle:{keywords:["animal","insect","nature","ladybug"],char:"🐞",fitzpatrick_scale:false,category:"animals_and_nature"},ant:{keywords:["animal","insect","nature","bug"],char:"🐜",fitzpatrick_scale:false,category:"animals_and_nature"},grasshopper:{keywords:["animal","cricket","chirp"],char:"🦗",fitzpatrick_scale:false,category:"animals_and_nature"},spider:{keywords:["animal","arachnid"],char:"🕷",fitzpatrick_scale:false,category:"animals_and_nature"},scorpion:{keywords:["animal","arachnid"],char:"🦂",fitzpatrick_scale:false,category:"animals_and_nature"},crab:{keywords:["animal","crustacean"],char:"🦀",fitzpatrick_scale:false,category:"animals_and_nature"},snake:{keywords:["animal","evil","nature","hiss","python"],char:"🐍",fitzpatrick_scale:false,category:"animals_and_nature"},lizard:{keywords:["animal","nature","reptile"],char:"🦎",fitzpatrick_scale:false,category:"animals_and_nature"},"t-rex":{keywords:["animal","nature","dinosaur","tyrannosaurus","extinct"],char:"🦖",fitzpatrick_scale:false,category:"animals_and_nature"},sauropod:{keywords:["animal","nature","dinosaur","brachiosaurus","brontosaurus","diplodocus","extinct"],char:"🦕",fitzpatrick_scale:false,category:"animals_and_nature"},turtle:{keywords:["animal","slow","nature","tortoise"],char:"🐢",fitzpatrick_scale:false,category:"animals_and_nature"},tropical_fish:{keywords:["animal","swim","ocean","beach","nemo"],char:"🐠",fitzpatrick_scale:false,category:"animals_and_nature"},fish:{keywords:["animal","food","nature"],char:"🐟",fitzpatrick_scale:false,category:"animals_and_nature"},blowfish:{keywords:["animal","nature","food","sea","ocean"],char:"🐡",fitzpatrick_scale:false,category:"animals_and_nature"},dolphin:{keywords:["animal","nature","fish","sea","ocean","flipper","fins","beach"],char:"🐬",fitzpatrick_scale:false,category:"animals_and_nature"},shark:{keywords:["animal","nature","fish","sea","ocean","jaws","fins","beach"],char:"🦈",fitzpatrick_scale:false,category:"animals_and_nature"},whale:{keywords:["animal","nature","sea","ocean"],char:"🐳",fitzpatrick_scale:false,category:"animals_and_nature"},whale2:{keywords:["animal","nature","sea","ocean"],char:"🐋",fitzpatrick_scale:false,category:"animals_and_nature"},crocodile:{keywords:["animal","nature","reptile","lizard","alligator"],char:"🐊",fitzpatrick_scale:false,category:"animals_and_nature"},leopard:{keywords:["animal","nature"],char:"🐆",fitzpatrick_scale:false,category:"animals_and_nature"},zebra:{keywords:["animal","nature","stripes","safari"],char:"🦓",fitzpatrick_scale:false,category:"animals_and_nature"},tiger2:{keywords:["animal","nature","roar"],char:"🐅",fitzpatrick_scale:false,category:"animals_and_nature"},water_buffalo:{keywords:["animal","nature","ox","cow"],char:"🐃",fitzpatrick_scale:false,category:"animals_and_nature"},ox:{keywords:["animal","cow","beef"],char:"🐂",fitzpatrick_scale:false,category:"animals_and_nature"},cow2:{keywords:["beef","ox","animal","nature","moo","milk"],char:"🐄",fitzpatrick_scale:false,category:"animals_and_nature"},deer:{keywords:["animal","nature","horns","venison"],char:"🦌",fitzpatrick_scale:false,category:"animals_and_nature"},dromedary_camel:{keywords:["animal","hot","desert","hump"],char:"🐪",fitzpatrick_scale:false,category:"animals_and_nature"},camel:{keywords:["animal","nature","hot","desert","hump"],char:"🐫",fitzpatrick_scale:false,category:"animals_and_nature"},giraffe:{keywords:["animal","nature","spots","safari"],char:"🦒",fitzpatrick_scale:false,category:"animals_and_nature"},elephant:{keywords:["animal","nature","nose","th","circus"],char:"🐘",fitzpatrick_scale:false,category:"animals_and_nature"},rhinoceros:{keywords:["animal","nature","horn"],char:"🦏",fitzpatrick_scale:false,category:"animals_and_nature"},goat:{keywords:["animal","nature"],char:"🐐",fitzpatrick_scale:false,category:"animals_and_nature"},ram:{keywords:["animal","sheep","nature"],char:"🐏",fitzpatrick_scale:false,category:"animals_and_nature"},sheep:{keywords:["animal","nature","wool","shipit"],char:"🐑",fitzpatrick_scale:false,category:"animals_and_nature"},racehorse:{keywords:["animal","gamble","luck"],char:"🐎",fitzpatrick_scale:false,category:"animals_and_nature"},pig2:{keywords:["animal","nature"],char:"🐖",fitzpatrick_scale:false,category:"animals_and_nature"},rat:{keywords:["animal","mouse","rodent"],char:"🐀",fitzpatrick_scale:false,category:"animals_and_nature"},mouse2:{keywords:["animal","nature","rodent"],char:"🐁",fitzpatrick_scale:false,category:"animals_and_nature"},rooster:{keywords:["animal","nature","chicken"],char:"🐓",fitzpatrick_scale:false,category:"animals_and_nature"},turkey:{keywords:["animal","bird"],char:"🦃",fitzpatrick_scale:false,category:"animals_and_nature"},dove:{keywords:["animal","bird"],char:"🕊",fitzpatrick_scale:false,category:"animals_and_nature"},dog2:{keywords:["animal","nature","friend","doge","pet","faithful"],char:"🐕",fitzpatrick_scale:false,category:"animals_and_nature"},poodle:{keywords:["dog","animal","101","nature","pet"],char:"🐩",fitzpatrick_scale:false,category:"animals_and_nature"},cat2:{keywords:["animal","meow","pet","cats"],char:"🐈",fitzpatrick_scale:false,category:"animals_and_nature"},rabbit2:{keywords:["animal","nature","pet","magic","spring"],char:"🐇",fitzpatrick_scale:false,category:"animals_and_nature"},chipmunk:{keywords:["animal","nature","rodent","squirrel"],char:"🐿",fitzpatrick_scale:false,category:"animals_and_nature"},hedgehog:{keywords:["animal","nature","spiny"],char:"🦔",fitzpatrick_scale:false,category:"animals_and_nature"},raccoon:{keywords:["animal","nature"],char:"🦝",fitzpatrick_scale:false,category:"animals_and_nature"},llama:{keywords:["animal","nature","alpaca"],char:"🦙",fitzpatrick_scale:false,category:"animals_and_nature"},hippopotamus:{keywords:["animal","nature"],char:"🦛",fitzpatrick_scale:false,category:"animals_and_nature"},kangaroo:{keywords:["animal","nature","australia","joey","hop","marsupial"],char:"🦘",fitzpatrick_scale:false,category:"animals_and_nature"},badger:{keywords:["animal","nature","honey"],char:"🦡",fitzpatrick_scale:false,category:"animals_and_nature"},swan:{keywords:["animal","nature","bird"],char:"🦢",fitzpatrick_scale:false,category:"animals_and_nature"},peacock:{keywords:["animal","nature","peahen","bird"],char:"🦚",fitzpatrick_scale:false,category:"animals_and_nature"},parrot:{keywords:["animal","nature","bird","pirate","talk"],char:"🦜",fitzpatrick_scale:false,category:"animals_and_nature"},lobster:{keywords:["animal","nature","bisque","claws","seafood"],char:"🦞",fitzpatrick_scale:false,category:"animals_and_nature"},mosquito:{keywords:["animal","nature","insect","malaria"],char:"🦟",fitzpatrick_scale:false,category:"animals_and_nature"},paw_prints:{keywords:["animal","tracking","footprints","dog","cat","pet","feet"],char:"🐾",fitzpatrick_scale:false,category:"animals_and_nature"},dragon:{keywords:["animal","myth","nature","chinese","green"],char:"🐉",fitzpatrick_scale:false,category:"animals_and_nature"},dragon_face:{keywords:["animal","myth","nature","chinese","green"],char:"🐲",fitzpatrick_scale:false,category:"animals_and_nature"},cactus:{keywords:["vegetable","plant","nature"],char:"🌵",fitzpatrick_scale:false,category:"animals_and_nature"},christmas_tree:{keywords:["festival","vacation","december","xmas","celebration"],char:"🎄",fitzpatrick_scale:false,category:"animals_and_nature"},evergreen_tree:{keywords:["plant","nature"],char:"🌲",fitzpatrick_scale:false,category:"animals_and_nature"},deciduous_tree:{keywords:["plant","nature"],char:"🌳",fitzpatrick_scale:false,category:"animals_and_nature"},palm_tree:{keywords:["plant","vegetable","nature","summer","beach","mojito","tropical"],char:"🌴",fitzpatrick_scale:false,category:"animals_and_nature"},seedling:{keywords:["plant","nature","grass","lawn","spring"],char:"🌱",fitzpatrick_scale:false,category:"animals_and_nature"},herb:{keywords:["vegetable","plant","medicine","weed","grass","lawn"],char:"🌿",fitzpatrick_scale:false,category:"animals_and_nature"},shamrock:{keywords:["vegetable","plant","nature","irish","clover"],char:"☘",fitzpatrick_scale:false,category:"animals_and_nature"},four_leaf_clover:{keywords:["vegetable","plant","nature","lucky","irish"],char:"🍀",fitzpatrick_scale:false,category:"animals_and_nature"},bamboo:{keywords:["plant","nature","vegetable","panda","pine_decoration"],char:"🎍",fitzpatrick_scale:false,category:"animals_and_nature"},tanabata_tree:{keywords:["plant","nature","branch","summer"],char:"🎋",fitzpatrick_scale:false,category:"animals_and_nature"},leaves:{keywords:["nature","plant","tree","vegetable","grass","lawn","spring"],char:"🍃",fitzpatrick_scale:false,category:"animals_and_nature"},fallen_leaf:{keywords:["nature","plant","vegetable","leaves"],char:"🍂",fitzpatrick_scale:false,category:"animals_and_nature"},maple_leaf:{keywords:["nature","plant","vegetable","ca","fall"],char:"🍁",fitzpatrick_scale:false,category:"animals_and_nature"},ear_of_rice:{keywords:["nature","plant"],char:"🌾",fitzpatrick_scale:false,category:"animals_and_nature"},hibiscus:{keywords:["plant","vegetable","flowers","beach"],char:"🌺",fitzpatrick_scale:false,category:"animals_and_nature"},sunflower:{keywords:["nature","plant","fall"],char:"🌻",fitzpatrick_scale:false,category:"animals_and_nature"},rose:{keywords:["flowers","valentines","love","spring"],char:"🌹",fitzpatrick_scale:false,category:"animals_and_nature"},wilted_flower:{keywords:["plant","nature","flower"],char:"🥀",fitzpatrick_scale:false,category:"animals_and_nature"},tulip:{keywords:["flowers","plant","nature","summer","spring"],char:"🌷",fitzpatrick_scale:false,category:"animals_and_nature"},blossom:{keywords:["nature","flowers","yellow"],char:"🌼",fitzpatrick_scale:false,category:"animals_and_nature"},cherry_blossom:{keywords:["nature","plant","spring","flower"],char:"🌸",fitzpatrick_scale:false,category:"animals_and_nature"},bouquet:{keywords:["flowers","nature","spring"],char:"💐",fitzpatrick_scale:false,category:"animals_and_nature"},mushroom:{keywords:["plant","vegetable"],char:"🍄",fitzpatrick_scale:false,category:"animals_and_nature"},chestnut:{keywords:["food","squirrel"],char:"🌰",fitzpatrick_scale:false,category:"animals_and_nature"},jack_o_lantern:{keywords:["halloween","light","pumpkin","creepy","fall"],char:"🎃",fitzpatrick_scale:false,category:"animals_and_nature"},shell:{keywords:["nature","sea","beach"],char:"🐚",fitzpatrick_scale:false,category:"animals_and_nature"},spider_web:{keywords:["animal","insect","arachnid","silk"],char:"🕸",fitzpatrick_scale:false,category:"animals_and_nature"},earth_americas:{keywords:["globe","world","USA","international"],char:"🌎",fitzpatrick_scale:false,category:"animals_and_nature"},earth_africa:{keywords:["globe","world","international"],char:"🌍",fitzpatrick_scale:false,category:"animals_and_nature"},earth_asia:{keywords:["globe","world","east","international"],char:"🌏",fitzpatrick_scale:false,category:"animals_and_nature"},full_moon:{keywords:["nature","yellow","twilight","planet","space","night","evening","sleep"],char:"🌕",fitzpatrick_scale:false,category:"animals_and_nature"},waning_gibbous_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep","waxing_gibbous_moon"],char:"🌖",fitzpatrick_scale:false,category:"animals_and_nature"},last_quarter_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"🌗",fitzpatrick_scale:false,category:"animals_and_nature"},waning_crescent_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"🌘",fitzpatrick_scale:false,category:"animals_and_nature"},new_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"🌑",fitzpatrick_scale:false,category:"animals_and_nature"},waxing_crescent_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"🌒",fitzpatrick_scale:false,category:"animals_and_nature"},first_quarter_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"🌓",fitzpatrick_scale:false,category:"animals_and_nature"},waxing_gibbous_moon:{keywords:["nature","night","sky","gray","twilight","planet","space","evening","sleep"],char:"🌔",fitzpatrick_scale:false,category:"animals_and_nature"},new_moon_with_face:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"🌚",fitzpatrick_scale:false,category:"animals_and_nature"},full_moon_with_face:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"🌝",fitzpatrick_scale:false,category:"animals_and_nature"},first_quarter_moon_with_face:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"🌛",fitzpatrick_scale:false,category:"animals_and_nature"},last_quarter_moon_with_face:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"🌜",fitzpatrick_scale:false,category:"animals_and_nature"},sun_with_face:{keywords:["nature","morning","sky"],char:"🌞",fitzpatrick_scale:false,category:"animals_and_nature"},crescent_moon:{keywords:["night","sleep","sky","evening","magic"],char:"🌙",fitzpatrick_scale:false,category:"animals_and_nature"},star:{keywords:["night","yellow"],char:"⭐",fitzpatrick_scale:false,category:"animals_and_nature"},star2:{keywords:["night","sparkle","awesome","good","magic"],char:"🌟",fitzpatrick_scale:false,category:"animals_and_nature"},dizzy:{keywords:["star","sparkle","shoot","magic"],char:"💫",fitzpatrick_scale:false,category:"animals_and_nature"},sparkles:{keywords:["stars","shine","shiny","cool","awesome","good","magic"],char:"✨",fitzpatrick_scale:false,category:"animals_and_nature"},comet:{keywords:["space"],char:"☄",fitzpatrick_scale:false,category:"animals_and_nature"},sunny:{keywords:["weather","nature","brightness","summer","beach","spring"],char:"☀️",fitzpatrick_scale:false,category:"animals_and_nature"},sun_behind_small_cloud:{keywords:["weather"],char:"🌤",fitzpatrick_scale:false,category:"animals_and_nature"},partly_sunny:{keywords:["weather","nature","cloudy","morning","fall","spring"],char:"⛅",fitzpatrick_scale:false,category:"animals_and_nature"},sun_behind_large_cloud:{keywords:["weather"],char:"🌥",fitzpatrick_scale:false,category:"animals_and_nature"},sun_behind_rain_cloud:{keywords:["weather"],char:"🌦",fitzpatrick_scale:false,category:"animals_and_nature"},cloud:{keywords:["weather","sky"],char:"☁️",fitzpatrick_scale:false,category:"animals_and_nature"},cloud_with_rain:{keywords:["weather"],char:"🌧",fitzpatrick_scale:false,category:"animals_and_nature"},cloud_with_lightning_and_rain:{keywords:["weather","lightning"],char:"⛈",fitzpatrick_scale:false,category:"animals_and_nature"},cloud_with_lightning:{keywords:["weather","thunder"],char:"🌩",fitzpatrick_scale:false,category:"animals_and_nature"},zap:{keywords:["thunder","weather","lightning bolt","fast"],char:"⚡",fitzpatrick_scale:false,category:"animals_and_nature"},fire:{keywords:["hot","cook","flame"],char:"🔥",fitzpatrick_scale:false,category:"animals_and_nature"},boom:{keywords:["bomb","explode","explosion","collision","blown"],char:"💥",fitzpatrick_scale:false,category:"animals_and_nature"},snowflake:{keywords:["winter","season","cold","weather","christmas","xmas"],char:"❄️",fitzpatrick_scale:false,category:"animals_and_nature"},cloud_with_snow:{keywords:["weather"],char:"🌨",fitzpatrick_scale:false,category:"animals_and_nature"},snowman:{keywords:["winter","season","cold","weather","christmas","xmas","frozen","without_snow"],char:"⛄",fitzpatrick_scale:false,category:"animals_and_nature"},snowman_with_snow:{keywords:["winter","season","cold","weather","christmas","xmas","frozen"],char:"☃",fitzpatrick_scale:false,category:"animals_and_nature"},wind_face:{keywords:["gust","air"],char:"🌬",fitzpatrick_scale:false,category:"animals_and_nature"},dash:{keywords:["wind","air","fast","shoo","fart","smoke","puff"],char:"💨",fitzpatrick_scale:false,category:"animals_and_nature"},tornado:{keywords:["weather","cyclone","twister"],char:"🌪",fitzpatrick_scale:false,category:"animals_and_nature"},fog:{keywords:["weather"],char:"🌫",fitzpatrick_scale:false,category:"animals_and_nature"},open_umbrella:{keywords:["weather","spring"],char:"☂",fitzpatrick_scale:false,category:"animals_and_nature"},umbrella:{keywords:["rainy","weather","spring"],char:"☔",fitzpatrick_scale:false,category:"animals_and_nature"},droplet:{keywords:["water","drip","faucet","spring"],char:"💧",fitzpatrick_scale:false,category:"animals_and_nature"},sweat_drops:{keywords:["water","drip","oops"],char:"💦",fitzpatrick_scale:false,category:"animals_and_nature"},ocean:{keywords:["sea","water","wave","nature","tsunami","disaster"],char:"🌊",fitzpatrick_scale:false,category:"animals_and_nature"},green_apple:{keywords:["fruit","nature"],char:"🍏",fitzpatrick_scale:false,category:"food_and_drink"},apple:{keywords:["fruit","mac","school"],char:"🍎",fitzpatrick_scale:false,category:"food_and_drink"},pear:{keywords:["fruit","nature","food"],char:"🍐",fitzpatrick_scale:false,category:"food_and_drink"},tangerine:{keywords:["food","fruit","nature","orange"],char:"🍊",fitzpatrick_scale:false,category:"food_and_drink"},lemon:{keywords:["fruit","nature"],char:"🍋",fitzpatrick_scale:false,category:"food_and_drink"},banana:{keywords:["fruit","food","monkey"],char:"🍌",fitzpatrick_scale:false,category:"food_and_drink"},watermelon:{keywords:["fruit","food","picnic","summer"],char:"🍉",fitzpatrick_scale:false,category:"food_and_drink"},grapes:{keywords:["fruit","food","wine"],char:"🍇",fitzpatrick_scale:false,category:"food_and_drink"},strawberry:{keywords:["fruit","food","nature"],char:"🍓",fitzpatrick_scale:false,category:"food_and_drink"},melon:{keywords:["fruit","nature","food"],char:"🍈",fitzpatrick_scale:false,category:"food_and_drink"},cherries:{keywords:["food","fruit"],char:"🍒",fitzpatrick_scale:false,category:"food_and_drink"},peach:{keywords:["fruit","nature","food"],char:"🍑",fitzpatrick_scale:false,category:"food_and_drink"},pineapple:{keywords:["fruit","nature","food"],char:"🍍",fitzpatrick_scale:false,category:"food_and_drink"},coconut:{keywords:["fruit","nature","food","palm"],char:"🥥",fitzpatrick_scale:false,category:"food_and_drink"},kiwi_fruit:{keywords:["fruit","food"],char:"🥝",fitzpatrick_scale:false,category:"food_and_drink"},mango:{keywords:["fruit","food","tropical"],char:"🥭",fitzpatrick_scale:false,category:"food_and_drink"},avocado:{keywords:["fruit","food"],char:"🥑",fitzpatrick_scale:false,category:"food_and_drink"},broccoli:{keywords:["fruit","food","vegetable"],char:"🥦",fitzpatrick_scale:false,category:"food_and_drink"},tomato:{keywords:["fruit","vegetable","nature","food"],char:"🍅",fitzpatrick_scale:false,category:"food_and_drink"},eggplant:{keywords:["vegetable","nature","food","aubergine"],char:"🍆",fitzpatrick_scale:false,category:"food_and_drink"},cucumber:{keywords:["fruit","food","pickle"],char:"🥒",fitzpatrick_scale:false,category:"food_and_drink"},carrot:{keywords:["vegetable","food","orange"],char:"🥕",fitzpatrick_scale:false,category:"food_and_drink"},hot_pepper:{keywords:["food","spicy","chilli","chili"],char:"🌶",fitzpatrick_scale:false,category:"food_and_drink"},potato:{keywords:["food","tuber","vegatable","starch"],char:"🥔",fitzpatrick_scale:false,category:"food_and_drink"},corn:{keywords:["food","vegetable","plant"],char:"🌽",fitzpatrick_scale:false,category:"food_and_drink"},leafy_greens:{keywords:["food","vegetable","plant","bok choy","cabbage","kale","lettuce"],char:"🥬",fitzpatrick_scale:false,category:"food_and_drink"},sweet_potato:{keywords:["food","nature"],char:"🍠",fitzpatrick_scale:false,category:"food_and_drink"},peanuts:{keywords:["food","nut"],char:"🥜",fitzpatrick_scale:false,category:"food_and_drink"},honey_pot:{keywords:["bees","sweet","kitchen"],char:"🍯",fitzpatrick_scale:false,category:"food_and_drink"},croissant:{keywords:["food","bread","french"],char:"🥐",fitzpatrick_scale:false,category:"food_and_drink"},bread:{keywords:["food","wheat","breakfast","toast"],char:"🍞",fitzpatrick_scale:false,category:"food_and_drink"},baguette_bread:{keywords:["food","bread","french"],char:"🥖",fitzpatrick_scale:false,category:"food_and_drink"},bagel:{keywords:["food","bread","bakery","schmear"],char:"🥯",fitzpatrick_scale:false,category:"food_and_drink"},pretzel:{keywords:["food","bread","twisted"],char:"🥨",fitzpatrick_scale:false,category:"food_and_drink"},cheese:{keywords:["food","chadder"],char:"🧀",fitzpatrick_scale:false,category:"food_and_drink"},egg:{keywords:["food","chicken","breakfast"],char:"🥚",fitzpatrick_scale:false,category:"food_and_drink"},bacon:{keywords:["food","breakfast","pork","pig","meat"],char:"🥓",fitzpatrick_scale:false,category:"food_and_drink"},steak:{keywords:["food","cow","meat","cut","chop","lambchop","porkchop"],char:"🥩",fitzpatrick_scale:false,category:"food_and_drink"},pancakes:{keywords:["food","breakfast","flapjacks","hotcakes"],char:"🥞",fitzpatrick_scale:false,category:"food_and_drink"},poultry_leg:{keywords:["food","meat","drumstick","bird","chicken","turkey"],char:"🍗",fitzpatrick_scale:false,category:"food_and_drink"},meat_on_bone:{keywords:["good","food","drumstick"],char:"🍖",fitzpatrick_scale:false,category:"food_and_drink"},bone:{keywords:["skeleton"],char:"🦴",fitzpatrick_scale:false,category:"food_and_drink"},fried_shrimp:{keywords:["food","animal","appetizer","summer"],char:"🍤",fitzpatrick_scale:false,category:"food_and_drink"},fried_egg:{keywords:["food","breakfast","kitchen","egg"],char:"🍳",fitzpatrick_scale:false,category:"food_and_drink"},hamburger:{keywords:["meat","fast food","beef","cheeseburger","mcdonalds","burger king"],char:"🍔",fitzpatrick_scale:false,category:"food_and_drink"},fries:{keywords:["chips","snack","fast food"],char:"🍟",fitzpatrick_scale:false,category:"food_and_drink"},stuffed_flatbread:{keywords:["food","flatbread","stuffed","gyro"],char:"🥙",fitzpatrick_scale:false,category:"food_and_drink"},hotdog:{keywords:["food","frankfurter"],char:"🌭",fitzpatrick_scale:false,category:"food_and_drink"},pizza:{keywords:["food","party"],char:"🍕",fitzpatrick_scale:false,category:"food_and_drink"},sandwich:{keywords:["food","lunch","bread"],char:"🥪",fitzpatrick_scale:false,category:"food_and_drink"},canned_food:{keywords:["food","soup"],char:"🥫",fitzpatrick_scale:false,category:"food_and_drink"},spaghetti:{keywords:["food","italian","noodle"],char:"🍝",fitzpatrick_scale:false,category:"food_and_drink"},taco:{keywords:["food","mexican"],char:"🌮",fitzpatrick_scale:false,category:"food_and_drink"},burrito:{keywords:["food","mexican"],char:"🌯",fitzpatrick_scale:false,category:"food_and_drink"},green_salad:{keywords:["food","healthy","lettuce"],char:"🥗",fitzpatrick_scale:false,category:"food_and_drink"},shallow_pan_of_food:{keywords:["food","cooking","casserole","paella"],char:"🥘",fitzpatrick_scale:false,category:"food_and_drink"},ramen:{keywords:["food","japanese","noodle","chopsticks"],char:"🍜",fitzpatrick_scale:false,category:"food_and_drink"},stew:{keywords:["food","meat","soup"],char:"🍲",fitzpatrick_scale:false,category:"food_and_drink"},fish_cake:{keywords:["food","japan","sea","beach","narutomaki","pink","swirl","kamaboko","surimi","ramen"],char:"🍥",fitzpatrick_scale:false,category:"food_and_drink"},fortune_cookie:{keywords:["food","prophecy"],char:"🥠",fitzpatrick_scale:false,category:"food_and_drink"},sushi:{keywords:["food","fish","japanese","rice"],char:"🍣",fitzpatrick_scale:false,category:"food_and_drink"},bento:{keywords:["food","japanese","box"],char:"🍱",fitzpatrick_scale:false,category:"food_and_drink"},curry:{keywords:["food","spicy","hot","indian"],char:"🍛",fitzpatrick_scale:false,category:"food_and_drink"},rice_ball:{keywords:["food","japanese"],char:"🍙",fitzpatrick_scale:false,category:"food_and_drink"},rice:{keywords:["food","china","asian"],char:"🍚",fitzpatrick_scale:false,category:"food_and_drink"},rice_cracker:{keywords:["food","japanese"],char:"🍘",fitzpatrick_scale:false,category:"food_and_drink"},oden:{keywords:["food","japanese"],char:"🍢",fitzpatrick_scale:false,category:"food_and_drink"},dango:{keywords:["food","dessert","sweet","japanese","barbecue","meat"],char:"🍡",fitzpatrick_scale:false,category:"food_and_drink"},shaved_ice:{keywords:["hot","dessert","summer"],char:"🍧",fitzpatrick_scale:false,category:"food_and_drink"},ice_cream:{keywords:["food","hot","dessert"],char:"🍨",fitzpatrick_scale:false,category:"food_and_drink"},icecream:{keywords:["food","hot","dessert","summer"],char:"🍦",fitzpatrick_scale:false,category:"food_and_drink"},pie:{keywords:["food","dessert","pastry"],char:"🥧",fitzpatrick_scale:false,category:"food_and_drink"},cake:{keywords:["food","dessert"],char:"🍰",fitzpatrick_scale:false,category:"food_and_drink"},cupcake:{keywords:["food","dessert","bakery","sweet"],char:"🧁",fitzpatrick_scale:false,category:"food_and_drink"},moon_cake:{keywords:["food","autumn"],char:"🥮",fitzpatrick_scale:false,category:"food_and_drink"},birthday:{keywords:["food","dessert","cake"],char:"🎂",fitzpatrick_scale:false,category:"food_and_drink"},custard:{keywords:["dessert","food"],char:"🍮",fitzpatrick_scale:false,category:"food_and_drink"},candy:{keywords:["snack","dessert","sweet","lolly"],char:"🍬",fitzpatrick_scale:false,category:"food_and_drink"},lollipop:{keywords:["food","snack","candy","sweet"],char:"🍭",fitzpatrick_scale:false,category:"food_and_drink"},chocolate_bar:{keywords:["food","snack","dessert","sweet"],char:"🍫",fitzpatrick_scale:false,category:"food_and_drink"},popcorn:{keywords:["food","movie theater","films","snack"],char:"🍿",fitzpatrick_scale:false,category:"food_and_drink"},dumpling:{keywords:["food","empanada","pierogi","potsticker"],char:"🥟",fitzpatrick_scale:false,category:"food_and_drink"},doughnut:{keywords:["food","dessert","snack","sweet","donut"],char:"🍩",fitzpatrick_scale:false,category:"food_and_drink"},cookie:{keywords:["food","snack","oreo","chocolate","sweet","dessert"],char:"🍪",fitzpatrick_scale:false,category:"food_and_drink"},milk_glass:{keywords:["beverage","drink","cow"],char:"🥛",fitzpatrick_scale:false,category:"food_and_drink"},beer:{keywords:["relax","beverage","drink","drunk","party","pub","summer","alcohol","booze"],char:"🍺",fitzpatrick_scale:false,category:"food_and_drink"},beers:{keywords:["relax","beverage","drink","drunk","party","pub","summer","alcohol","booze"],char:"🍻",fitzpatrick_scale:false,category:"food_and_drink"},clinking_glasses:{keywords:["beverage","drink","party","alcohol","celebrate","cheers","wine","champagne","toast"],char:"🥂",fitzpatrick_scale:false,category:"food_and_drink"},wine_glass:{keywords:["drink","beverage","drunk","alcohol","booze"],char:"🍷",fitzpatrick_scale:false,category:"food_and_drink"},tumbler_glass:{keywords:["drink","beverage","drunk","alcohol","liquor","booze","bourbon","scotch","whisky","glass","shot"],char:"🥃",fitzpatrick_scale:false,category:"food_and_drink"},cocktail:{keywords:["drink","drunk","alcohol","beverage","booze","mojito"],char:"🍸",fitzpatrick_scale:false,category:"food_and_drink"},tropical_drink:{keywords:["beverage","cocktail","summer","beach","alcohol","booze","mojito"],char:"🍹",fitzpatrick_scale:false,category:"food_and_drink"},champagne:{keywords:["drink","wine","bottle","celebration"],char:"🍾",fitzpatrick_scale:false,category:"food_and_drink"},sake:{keywords:["wine","drink","drunk","beverage","japanese","alcohol","booze"],char:"🍶",fitzpatrick_scale:false,category:"food_and_drink"},tea:{keywords:["drink","bowl","breakfast","green","british"],char:"🍵",fitzpatrick_scale:false,category:"food_and_drink"},cup_with_straw:{keywords:["drink","soda"],char:"🥤",fitzpatrick_scale:false,category:"food_and_drink"},coffee:{keywords:["beverage","caffeine","latte","espresso"],char:"☕",fitzpatrick_scale:false,category:"food_and_drink"},baby_bottle:{keywords:["food","container","milk"],char:"🍼",fitzpatrick_scale:false,category:"food_and_drink"},salt:{keywords:["condiment","shaker"],char:"🧂",fitzpatrick_scale:false,category:"food_and_drink"},spoon:{keywords:["cutlery","kitchen","tableware"],char:"🥄",fitzpatrick_scale:false,category:"food_and_drink"},fork_and_knife:{keywords:["cutlery","kitchen"],char:"🍴",fitzpatrick_scale:false,category:"food_and_drink"},plate_with_cutlery:{keywords:["food","eat","meal","lunch","dinner","restaurant"],char:"🍽",fitzpatrick_scale:false,category:"food_and_drink"},bowl_with_spoon:{keywords:["food","breakfast","cereal","oatmeal","porridge"],char:"🥣",fitzpatrick_scale:false,category:"food_and_drink"},takeout_box:{keywords:["food","leftovers"],char:"🥡",fitzpatrick_scale:false,category:"food_and_drink"},chopsticks:{keywords:["food"],char:"🥢",fitzpatrick_scale:false,category:"food_and_drink"},soccer:{keywords:["sports","football"],char:"⚽",fitzpatrick_scale:false,category:"activity"},basketball:{keywords:["sports","balls","NBA"],char:"🏀",fitzpatrick_scale:false,category:"activity"},football:{keywords:["sports","balls","NFL"],char:"🏈",fitzpatrick_scale:false,category:"activity"},baseball:{keywords:["sports","balls"],char:"⚾",fitzpatrick_scale:false,category:"activity"},softball:{keywords:["sports","balls"],char:"🥎",fitzpatrick_scale:false,category:"activity"},tennis:{keywords:["sports","balls","green"],char:"🎾",fitzpatrick_scale:false,category:"activity"},volleyball:{keywords:["sports","balls"],char:"🏐",fitzpatrick_scale:false,category:"activity"},rugby_football:{keywords:["sports","team"],char:"🏉",fitzpatrick_scale:false,category:"activity"},flying_disc:{keywords:["sports","frisbee","ultimate"],char:"🥏",fitzpatrick_scale:false,category:"activity"},"8ball":{keywords:["pool","hobby","game","luck","magic"],char:"🎱",fitzpatrick_scale:false,category:"activity"},golf:{keywords:["sports","business","flag","hole","summer"],char:"⛳",fitzpatrick_scale:false,category:"activity"},golfing_woman:{keywords:["sports","business","woman","female"],char:"🏌️‍♀️",fitzpatrick_scale:false,category:"activity"},golfing_man:{keywords:["sports","business"],char:"🏌",fitzpatrick_scale:true,category:"activity"},ping_pong:{keywords:["sports","pingpong"],char:"🏓",fitzpatrick_scale:false,category:"activity"},badminton:{keywords:["sports"],char:"🏸",fitzpatrick_scale:false,category:"activity"},goal_net:{keywords:["sports"],char:"🥅",fitzpatrick_scale:false,category:"activity"},ice_hockey:{keywords:["sports"],char:"🏒",fitzpatrick_scale:false,category:"activity"},field_hockey:{keywords:["sports"],char:"🏑",fitzpatrick_scale:false,category:"activity"},lacrosse:{keywords:["sports","ball","stick"],char:"🥍",fitzpatrick_scale:false,category:"activity"},cricket:{keywords:["sports"],char:"🏏",fitzpatrick_scale:false,category:"activity"},ski:{keywords:["sports","winter","cold","snow"],char:"🎿",fitzpatrick_scale:false,category:"activity"},skier:{keywords:["sports","winter","snow"],char:"⛷",fitzpatrick_scale:false,category:"activity"},snowboarder:{keywords:["sports","winter"],char:"🏂",fitzpatrick_scale:true,category:"activity"},person_fencing:{keywords:["sports","fencing","sword"],char:"🤺",fitzpatrick_scale:false,category:"activity"},women_wrestling:{keywords:["sports","wrestlers"],char:"🤼‍♀️",fitzpatrick_scale:false,category:"activity"},men_wrestling:{keywords:["sports","wrestlers"],char:"🤼‍♂️",fitzpatrick_scale:false,category:"activity"},woman_cartwheeling:{keywords:["gymnastics"],char:"🤸‍♀️",fitzpatrick_scale:true,category:"activity"},man_cartwheeling:{keywords:["gymnastics"],char:"🤸‍♂️",fitzpatrick_scale:true,category:"activity"},woman_playing_handball:{keywords:["sports"],char:"🤾‍♀️",fitzpatrick_scale:true,category:"activity"},man_playing_handball:{keywords:["sports"],char:"🤾‍♂️",fitzpatrick_scale:true,category:"activity"},ice_skate:{keywords:["sports"],char:"⛸",fitzpatrick_scale:false,category:"activity"},curling_stone:{keywords:["sports"],char:"🥌",fitzpatrick_scale:false,category:"activity"},skateboard:{keywords:["board"],char:"🛹",fitzpatrick_scale:false,category:"activity"},sled:{keywords:["sleigh","luge","toboggan"],char:"🛷",fitzpatrick_scale:false,category:"activity"},bow_and_arrow:{keywords:["sports"],char:"🏹",fitzpatrick_scale:false,category:"activity"},fishing_pole_and_fish:{keywords:["food","hobby","summer"],char:"🎣",fitzpatrick_scale:false,category:"activity"},boxing_glove:{keywords:["sports","fighting"],char:"🥊",fitzpatrick_scale:false,category:"activity"},martial_arts_uniform:{keywords:["judo","karate","taekwondo"],char:"🥋",fitzpatrick_scale:false,category:"activity"},rowing_woman:{keywords:["sports","hobby","water","ship","woman","female"],char:"🚣‍♀️",fitzpatrick_scale:true,category:"activity"},rowing_man:{keywords:["sports","hobby","water","ship"],char:"🚣",fitzpatrick_scale:true,category:"activity"},climbing_woman:{keywords:["sports","hobby","woman","female","rock"],char:"🧗‍♀️",fitzpatrick_scale:true,category:"activity"},climbing_man:{keywords:["sports","hobby","man","male","rock"],char:"🧗‍♂️",fitzpatrick_scale:true,category:"activity"},swimming_woman:{keywords:["sports","exercise","human","athlete","water","summer","woman","female"],char:"🏊‍♀️",fitzpatrick_scale:true,category:"activity"},swimming_man:{keywords:["sports","exercise","human","athlete","water","summer"],char:"🏊",fitzpatrick_scale:true,category:"activity"},woman_playing_water_polo:{keywords:["sports","pool"],char:"🤽‍♀️",fitzpatrick_scale:true,category:"activity"},man_playing_water_polo:{keywords:["sports","pool"],char:"🤽‍♂️",fitzpatrick_scale:true,category:"activity"},woman_in_lotus_position:{keywords:["woman","female","meditation","yoga","serenity","zen","mindfulness"],char:"🧘‍♀️",fitzpatrick_scale:true,category:"activity"},man_in_lotus_position:{keywords:["man","male","meditation","yoga","serenity","zen","mindfulness"],char:"🧘‍♂️",fitzpatrick_scale:true,category:"activity"},surfing_woman:{keywords:["sports","ocean","sea","summer","beach","woman","female"],char:"🏄‍♀️",fitzpatrick_scale:true,category:"activity"},surfing_man:{keywords:["sports","ocean","sea","summer","beach"],char:"🏄",fitzpatrick_scale:true,category:"activity"},bath:{keywords:["clean","shower","bathroom"],char:"🛀",fitzpatrick_scale:true,category:"activity"},basketball_woman:{keywords:["sports","human","woman","female"],char:"⛹️‍♀️",fitzpatrick_scale:true,category:"activity"},basketball_man:{keywords:["sports","human"],char:"⛹",fitzpatrick_scale:true,category:"activity"},weight_lifting_woman:{keywords:["sports","training","exercise","woman","female"],char:"🏋️‍♀️",fitzpatrick_scale:true,category:"activity"},weight_lifting_man:{keywords:["sports","training","exercise"],char:"🏋",fitzpatrick_scale:true,category:"activity"},biking_woman:{keywords:["sports","bike","exercise","hipster","woman","female"],char:"🚴‍♀️",fitzpatrick_scale:true,category:"activity"},biking_man:{keywords:["sports","bike","exercise","hipster"],char:"🚴",fitzpatrick_scale:true,category:"activity"},mountain_biking_woman:{keywords:["transportation","sports","human","race","bike","woman","female"],char:"🚵‍♀️",fitzpatrick_scale:true,category:"activity"},mountain_biking_man:{keywords:["transportation","sports","human","race","bike"],char:"🚵",fitzpatrick_scale:true,category:"activity"},horse_racing:{keywords:["animal","betting","competition","gambling","luck"],char:"🏇",fitzpatrick_scale:true,category:"activity"},business_suit_levitating:{keywords:["suit","business","levitate","hover","jump"],char:"🕴",fitzpatrick_scale:true,category:"activity"},trophy:{keywords:["win","award","contest","place","ftw","ceremony"],char:"🏆",fitzpatrick_scale:false,category:"activity"},running_shirt_with_sash:{keywords:["play","pageant"],char:"🎽",fitzpatrick_scale:false,category:"activity"},medal_sports:{keywords:["award","winning"],char:"🏅",fitzpatrick_scale:false,category:"activity"},medal_military:{keywords:["award","winning","army"],char:"🎖",fitzpatrick_scale:false,category:"activity"},"1st_place_medal":{keywords:["award","winning","first"],char:"🥇",fitzpatrick_scale:false,category:"activity"},"2nd_place_medal":{keywords:["award","second"],char:"🥈",fitzpatrick_scale:false,category:"activity"},"3rd_place_medal":{keywords:["award","third"],char:"🥉",fitzpatrick_scale:false,category:"activity"},reminder_ribbon:{keywords:["sports","cause","support","awareness"],char:"🎗",fitzpatrick_scale:false,category:"activity"},rosette:{keywords:["flower","decoration","military"],char:"🏵",fitzpatrick_scale:false,category:"activity"},ticket:{keywords:["event","concert","pass"],char:"🎫",fitzpatrick_scale:false,category:"activity"},tickets:{keywords:["sports","concert","entrance"],char:"🎟",fitzpatrick_scale:false,category:"activity"},performing_arts:{keywords:["acting","theater","drama"],char:"🎭",fitzpatrick_scale:false,category:"activity"},art:{keywords:["design","paint","draw","colors"],char:"🎨",fitzpatrick_scale:false,category:"activity"},circus_tent:{keywords:["festival","carnival","party"],char:"🎪",fitzpatrick_scale:false,category:"activity"},woman_juggling:{keywords:["juggle","balance","skill","multitask"],char:"🤹‍♀️",fitzpatrick_scale:true,category:"activity"},man_juggling:{keywords:["juggle","balance","skill","multitask"],char:"🤹‍♂️",fitzpatrick_scale:true,category:"activity"},microphone:{keywords:["sound","music","PA","sing","talkshow"],char:"🎤",fitzpatrick_scale:false,category:"activity"},headphones:{keywords:["music","score","gadgets"],char:"🎧",fitzpatrick_scale:false,category:"activity"},musical_score:{keywords:["treble","clef","compose"],char:"🎼",fitzpatrick_scale:false,category:"activity"},musical_keyboard:{keywords:["piano","instrument","compose"],char:"🎹",fitzpatrick_scale:false,category:"activity"},drum:{keywords:["music","instrument","drumsticks","snare"],char:"🥁",fitzpatrick_scale:false,category:"activity"},saxophone:{keywords:["music","instrument","jazz","blues"],char:"🎷",fitzpatrick_scale:false,category:"activity"},trumpet:{keywords:["music","brass"],char:"🎺",fitzpatrick_scale:false,category:"activity"},guitar:{keywords:["music","instrument"],char:"🎸",fitzpatrick_scale:false,category:"activity"},violin:{keywords:["music","instrument","orchestra","symphony"],char:"🎻",fitzpatrick_scale:false,category:"activity"},clapper:{keywords:["movie","film","record"],char:"🎬",fitzpatrick_scale:false,category:"activity"},video_game:{keywords:["play","console","PS4","controller"],char:"🎮",fitzpatrick_scale:false,category:"activity"},space_invader:{keywords:["game","arcade","play"],char:"👾",fitzpatrick_scale:false,category:"activity"},dart:{keywords:["game","play","bar","target","bullseye"],char:"🎯",fitzpatrick_scale:false,category:"activity"},game_die:{keywords:["dice","random","tabletop","play","luck"],char:"🎲",fitzpatrick_scale:false,category:"activity"},chess_pawn:{keywords:["expendable"],char:"♟",fitzpatrick_scale:false,category:"activity"},slot_machine:{keywords:["bet","gamble","vegas","fruit machine","luck","casino"],char:"🎰",fitzpatrick_scale:false,category:"activity"},jigsaw:{keywords:["interlocking","puzzle","piece"],char:"🧩",fitzpatrick_scale:false,category:"activity"},bowling:{keywords:["sports","fun","play"],char:"🎳",fitzpatrick_scale:false,category:"activity"},red_car:{keywords:["red","transportation","vehicle"],char:"🚗",fitzpatrick_scale:false,category:"travel_and_places"},taxi:{keywords:["uber","vehicle","cars","transportation"],char:"🚕",fitzpatrick_scale:false,category:"travel_and_places"},blue_car:{keywords:["transportation","vehicle"],char:"🚙",fitzpatrick_scale:false,category:"travel_and_places"},bus:{keywords:["car","vehicle","transportation"],char:"🚌",fitzpatrick_scale:false,category:"travel_and_places"},trolleybus:{keywords:["bart","transportation","vehicle"],char:"🚎",fitzpatrick_scale:false,category:"travel_and_places"},racing_car:{keywords:["sports","race","fast","formula","f1"],char:"🏎",fitzpatrick_scale:false,category:"travel_and_places"},police_car:{keywords:["vehicle","cars","transportation","law","legal","enforcement"],char:"🚓",fitzpatrick_scale:false,category:"travel_and_places"},ambulance:{keywords:["health","911","hospital"],char:"🚑",fitzpatrick_scale:false,category:"travel_and_places"},fire_engine:{keywords:["transportation","cars","vehicle"],char:"🚒",fitzpatrick_scale:false,category:"travel_and_places"},minibus:{keywords:["vehicle","car","transportation"],char:"🚐",fitzpatrick_scale:false,category:"travel_and_places"},truck:{keywords:["cars","transportation"],char:"🚚",fitzpatrick_scale:false,category:"travel_and_places"},articulated_lorry:{keywords:["vehicle","cars","transportation","express"],char:"🚛",fitzpatrick_scale:false,category:"travel_and_places"},tractor:{keywords:["vehicle","car","farming","agriculture"],char:"🚜",fitzpatrick_scale:false,category:"travel_and_places"},kick_scooter:{keywords:["vehicle","kick","razor"],char:"🛴",fitzpatrick_scale:false,category:"travel_and_places"},motorcycle:{keywords:["race","sports","fast"],char:"🏍",fitzpatrick_scale:false,category:"travel_and_places"},bike:{keywords:["sports","bicycle","exercise","hipster"],char:"🚲",fitzpatrick_scale:false,category:"travel_and_places"},motor_scooter:{keywords:["vehicle","vespa","sasha"],char:"🛵",fitzpatrick_scale:false,category:"travel_and_places"},rotating_light:{keywords:["police","ambulance","911","emergency","alert","error","pinged","law","legal"],char:"🚨",fitzpatrick_scale:false,category:"travel_and_places"},oncoming_police_car:{keywords:["vehicle","law","legal","enforcement","911"],char:"🚔",fitzpatrick_scale:false,category:"travel_and_places"},oncoming_bus:{keywords:["vehicle","transportation"],char:"🚍",fitzpatrick_scale:false,category:"travel_and_places"},oncoming_automobile:{keywords:["car","vehicle","transportation"],char:"🚘",fitzpatrick_scale:false,category:"travel_and_places"},oncoming_taxi:{keywords:["vehicle","cars","uber"],char:"🚖",fitzpatrick_scale:false,category:"travel_and_places"},aerial_tramway:{keywords:["transportation","vehicle","ski"],char:"🚡",fitzpatrick_scale:false,category:"travel_and_places"},mountain_cableway:{keywords:["transportation","vehicle","ski"],char:"🚠",fitzpatrick_scale:false,category:"travel_and_places"},suspension_railway:{keywords:["vehicle","transportation"],char:"🚟",fitzpatrick_scale:false,category:"travel_and_places"},railway_car:{keywords:["transportation","vehicle"],char:"🚃",fitzpatrick_scale:false,category:"travel_and_places"},train:{keywords:["transportation","vehicle","carriage","public","travel"],char:"🚋",fitzpatrick_scale:false,category:"travel_and_places"},monorail:{keywords:["transportation","vehicle"],char:"🚝",fitzpatrick_scale:false,category:"travel_and_places"},bullettrain_side:{keywords:["transportation","vehicle"],char:"🚄",fitzpatrick_scale:false,category:"travel_and_places"},bullettrain_front:{keywords:["transportation","vehicle","speed","fast","public","travel"],char:"🚅",fitzpatrick_scale:false,category:"travel_and_places"},light_rail:{keywords:["transportation","vehicle"],char:"🚈",fitzpatrick_scale:false,category:"travel_and_places"},mountain_railway:{keywords:["transportation","vehicle"],char:"🚞",fitzpatrick_scale:false,category:"travel_and_places"},steam_locomotive:{keywords:["transportation","vehicle","train"],char:"🚂",fitzpatrick_scale:false,category:"travel_and_places"},train2:{keywords:["transportation","vehicle"],char:"🚆",fitzpatrick_scale:false,category:"travel_and_places"},metro:{keywords:["transportation","blue-square","mrt","underground","tube"],char:"🚇",fitzpatrick_scale:false,category:"travel_and_places"},tram:{keywords:["transportation","vehicle"],char:"🚊",fitzpatrick_scale:false,category:"travel_and_places"},station:{keywords:["transportation","vehicle","public"],char:"🚉",fitzpatrick_scale:false,category:"travel_and_places"},flying_saucer:{keywords:["transportation","vehicle","ufo"],char:"🛸",fitzpatrick_scale:false,category:"travel_and_places"},helicopter:{keywords:["transportation","vehicle","fly"],char:"🚁",fitzpatrick_scale:false,category:"travel_and_places"},small_airplane:{keywords:["flight","transportation","fly","vehicle"],char:"🛩",fitzpatrick_scale:false,category:"travel_and_places"},airplane:{keywords:["vehicle","transportation","flight","fly"],char:"✈️",fitzpatrick_scale:false,category:"travel_and_places"},flight_departure:{keywords:["airport","flight","landing"],char:"🛫",fitzpatrick_scale:false,category:"travel_and_places"},flight_arrival:{keywords:["airport","flight","boarding"],char:"🛬",fitzpatrick_scale:false,category:"travel_and_places"},sailboat:{keywords:["ship","summer","transportation","water","sailing"],char:"⛵",fitzpatrick_scale:false,category:"travel_and_places"},motor_boat:{keywords:["ship"],char:"🛥",fitzpatrick_scale:false,category:"travel_and_places"},speedboat:{keywords:["ship","transportation","vehicle","summer"],char:"🚤",fitzpatrick_scale:false,category:"travel_and_places"},ferry:{keywords:["boat","ship","yacht"],char:"⛴",fitzpatrick_scale:false,category:"travel_and_places"},passenger_ship:{keywords:["yacht","cruise","ferry"],char:"🛳",fitzpatrick_scale:false,category:"travel_and_places"},rocket:{keywords:["launch","ship","staffmode","NASA","outer space","outer_space","fly"],char:"🚀",fitzpatrick_scale:false,category:"travel_and_places"},artificial_satellite:{keywords:["communication","gps","orbit","spaceflight","NASA","ISS"],char:"🛰",fitzpatrick_scale:false,category:"travel_and_places"},seat:{keywords:["sit","airplane","transport","bus","flight","fly"],char:"💺",fitzpatrick_scale:false,category:"travel_and_places"},canoe:{keywords:["boat","paddle","water","ship"],char:"🛶",fitzpatrick_scale:false,category:"travel_and_places"},anchor:{keywords:["ship","ferry","sea","boat"],char:"⚓",fitzpatrick_scale:false,category:"travel_and_places"},construction:{keywords:["wip","progress","caution","warning"],char:"🚧",fitzpatrick_scale:false,category:"travel_and_places"},fuelpump:{keywords:["gas station","petroleum"],char:"⛽",fitzpatrick_scale:false,category:"travel_and_places"},busstop:{keywords:["transportation","wait"],char:"🚏",fitzpatrick_scale:false,category:"travel_and_places"},vertical_traffic_light:{keywords:["transportation","driving"],char:"🚦",fitzpatrick_scale:false,category:"travel_and_places"},traffic_light:{keywords:["transportation","signal"],char:"🚥",fitzpatrick_scale:false,category:"travel_and_places"},checkered_flag:{keywords:["contest","finishline","race","gokart"],char:"🏁",fitzpatrick_scale:false,category:"travel_and_places"},ship:{keywords:["transportation","titanic","deploy"],char:"🚢",fitzpatrick_scale:false,category:"travel_and_places"},ferris_wheel:{keywords:["photo","carnival","londoneye"],char:"🎡",fitzpatrick_scale:false,category:"travel_and_places"},roller_coaster:{keywords:["carnival","playground","photo","fun"],char:"🎢",fitzpatrick_scale:false,category:"travel_and_places"},carousel_horse:{keywords:["photo","carnival"],char:"🎠",fitzpatrick_scale:false,category:"travel_and_places"},building_construction:{keywords:["wip","working","progress"],char:"🏗",fitzpatrick_scale:false,category:"travel_and_places"},foggy:{keywords:["photo","mountain"],char:"🌁",fitzpatrick_scale:false,category:"travel_and_places"},tokyo_tower:{keywords:["photo","japanese"],char:"🗼",fitzpatrick_scale:false,category:"travel_and_places"},factory:{keywords:["building","industry","pollution","smoke"],char:"🏭",fitzpatrick_scale:false,category:"travel_and_places"},fountain:{keywords:["photo","summer","water","fresh"],char:"⛲",fitzpatrick_scale:false,category:"travel_and_places"},rice_scene:{keywords:["photo","japan","asia","tsukimi"],char:"🎑",fitzpatrick_scale:false,category:"travel_and_places"},mountain:{keywords:["photo","nature","environment"],char:"⛰",fitzpatrick_scale:false,category:"travel_and_places"},mountain_snow:{keywords:["photo","nature","environment","winter","cold"],char:"🏔",fitzpatrick_scale:false,category:"travel_and_places"},mount_fuji:{keywords:["photo","mountain","nature","japanese"],char:"🗻",fitzpatrick_scale:false,category:"travel_and_places"},volcano:{keywords:["photo","nature","disaster"],char:"🌋",fitzpatrick_scale:false,category:"travel_and_places"},japan:{keywords:["nation","country","japanese","asia"],char:"🗾",fitzpatrick_scale:false,category:"travel_and_places"},camping:{keywords:["photo","outdoors","tent"],char:"🏕",fitzpatrick_scale:false,category:"travel_and_places"},tent:{keywords:["photo","camping","outdoors"],char:"⛺",fitzpatrick_scale:false,category:"travel_and_places"},national_park:{keywords:["photo","environment","nature"],char:"🏞",fitzpatrick_scale:false,category:"travel_and_places"},motorway:{keywords:["road","cupertino","interstate","highway"],char:"🛣",fitzpatrick_scale:false,category:"travel_and_places"},railway_track:{keywords:["train","transportation"],char:"🛤",fitzpatrick_scale:false,category:"travel_and_places"},sunrise:{keywords:["morning","view","vacation","photo"],char:"🌅",fitzpatrick_scale:false,category:"travel_and_places"},sunrise_over_mountains:{keywords:["view","vacation","photo"],char:"🌄",fitzpatrick_scale:false,category:"travel_and_places"},desert:{keywords:["photo","warm","saharah"],char:"🏜",fitzpatrick_scale:false,category:"travel_and_places"},beach_umbrella:{keywords:["weather","summer","sunny","sand","mojito"],char:"🏖",fitzpatrick_scale:false,category:"travel_and_places"},desert_island:{keywords:["photo","tropical","mojito"],char:"🏝",fitzpatrick_scale:false,category:"travel_and_places"},city_sunrise:{keywords:["photo","good morning","dawn"],char:"🌇",fitzpatrick_scale:false,category:"travel_and_places"},city_sunset:{keywords:["photo","evening","sky","buildings"],char:"🌆",fitzpatrick_scale:false,category:"travel_and_places"},cityscape:{keywords:["photo","night life","urban"],char:"🏙",fitzpatrick_scale:false,category:"travel_and_places"},night_with_stars:{keywords:["evening","city","downtown"],char:"🌃",fitzpatrick_scale:false,category:"travel_and_places"},bridge_at_night:{keywords:["photo","sanfrancisco"],char:"🌉",fitzpatrick_scale:false,category:"travel_and_places"},milky_way:{keywords:["photo","space","stars"],char:"🌌",fitzpatrick_scale:false,category:"travel_and_places"},stars:{keywords:["night","photo"],char:"🌠",fitzpatrick_scale:false,category:"travel_and_places"},sparkler:{keywords:["stars","night","shine"],char:"🎇",fitzpatrick_scale:false,category:"travel_and_places"},fireworks:{keywords:["photo","festival","carnival","congratulations"],char:"🎆",fitzpatrick_scale:false,category:"travel_and_places"},rainbow:{keywords:["nature","happy","unicorn_face","photo","sky","spring"],char:"🌈",fitzpatrick_scale:false,category:"travel_and_places"},houses:{keywords:["buildings","photo"],char:"🏘",fitzpatrick_scale:false,category:"travel_and_places"},european_castle:{keywords:["building","royalty","history"],char:"🏰",fitzpatrick_scale:false,category:"travel_and_places"},japanese_castle:{keywords:["photo","building"],char:"🏯",fitzpatrick_scale:false,category:"travel_and_places"},stadium:{keywords:["photo","place","sports","concert","venue"],char:"🏟",fitzpatrick_scale:false,category:"travel_and_places"},statue_of_liberty:{keywords:["american","newyork"],char:"🗽",fitzpatrick_scale:false,category:"travel_and_places"},house:{keywords:["building","home"],char:"🏠",fitzpatrick_scale:false,category:"travel_and_places"},house_with_garden:{keywords:["home","plant","nature"],char:"🏡",fitzpatrick_scale:false,category:"travel_and_places"},derelict_house:{keywords:["abandon","evict","broken","building"],char:"🏚",fitzpatrick_scale:false,category:"travel_and_places"},office:{keywords:["building","bureau","work"],char:"🏢",fitzpatrick_scale:false,category:"travel_and_places"},department_store:{keywords:["building","shopping","mall"],char:"🏬",fitzpatrick_scale:false,category:"travel_and_places"},post_office:{keywords:["building","envelope","communication"],char:"🏣",fitzpatrick_scale:false,category:"travel_and_places"},european_post_office:{keywords:["building","email"],char:"🏤",fitzpatrick_scale:false,category:"travel_and_places"},hospital:{keywords:["building","health","surgery","doctor"],char:"🏥",fitzpatrick_scale:false,category:"travel_and_places"},bank:{keywords:["building","money","sales","cash","business","enterprise"],char:"🏦",fitzpatrick_scale:false,category:"travel_and_places"},hotel:{keywords:["building","accomodation","checkin"],char:"🏨",fitzpatrick_scale:false,category:"travel_and_places"},convenience_store:{keywords:["building","shopping","groceries"],char:"🏪",fitzpatrick_scale:false,category:"travel_and_places"},school:{keywords:["building","student","education","learn","teach"],char:"🏫",fitzpatrick_scale:false,category:"travel_and_places"},love_hotel:{keywords:["like","affection","dating"],char:"🏩",fitzpatrick_scale:false,category:"travel_and_places"},wedding:{keywords:["love","like","affection","couple","marriage","bride","groom"],char:"💒",fitzpatrick_scale:false,category:"travel_and_places"},classical_building:{keywords:["art","culture","history"],char:"🏛",fitzpatrick_scale:false,category:"travel_and_places"},church:{keywords:["building","religion","christ"],char:"⛪",fitzpatrick_scale:false,category:"travel_and_places"},mosque:{keywords:["islam","worship","minaret"],char:"🕌",fitzpatrick_scale:false,category:"travel_and_places"},synagogue:{keywords:["judaism","worship","temple","jewish"],char:"🕍",fitzpatrick_scale:false,category:"travel_and_places"},kaaba:{keywords:["mecca","mosque","islam"],char:"🕋",fitzpatrick_scale:false,category:"travel_and_places"},shinto_shrine:{keywords:["temple","japan","kyoto"],char:"⛩",fitzpatrick_scale:false,category:"travel_and_places"},watch:{keywords:["time","accessories"],char:"⌚",fitzpatrick_scale:false,category:"objects"},iphone:{keywords:["technology","apple","gadgets","dial"],char:"📱",fitzpatrick_scale:false,category:"objects"},calling:{keywords:["iphone","incoming"],char:"📲",fitzpatrick_scale:false,category:"objects"},computer:{keywords:["technology","laptop","screen","display","monitor"],char:"💻",fitzpatrick_scale:false,category:"objects"},keyboard:{keywords:["technology","computer","type","input","text"],char:"⌨",fitzpatrick_scale:false,category:"objects"},desktop_computer:{keywords:["technology","computing","screen"],char:"🖥",fitzpatrick_scale:false,category:"objects"},printer:{keywords:["paper","ink"],char:"🖨",fitzpatrick_scale:false,category:"objects"},computer_mouse:{keywords:["click"],char:"🖱",fitzpatrick_scale:false,category:"objects"},trackball:{keywords:["technology","trackpad"],char:"🖲",fitzpatrick_scale:false,category:"objects"},joystick:{keywords:["game","play"],char:"🕹",fitzpatrick_scale:false,category:"objects"},clamp:{keywords:["tool"],char:"🗜",fitzpatrick_scale:false,category:"objects"},minidisc:{keywords:["technology","record","data","disk","90s"],char:"💽",fitzpatrick_scale:false,category:"objects"},floppy_disk:{keywords:["oldschool","technology","save","90s","80s"],char:"💾",fitzpatrick_scale:false,category:"objects"},cd:{keywords:["technology","dvd","disk","disc","90s"],char:"💿",fitzpatrick_scale:false,category:"objects"},dvd:{keywords:["cd","disk","disc"],char:"📀",fitzpatrick_scale:false,category:"objects"},vhs:{keywords:["record","video","oldschool","90s","80s"],char:"📼",fitzpatrick_scale:false,category:"objects"},camera:{keywords:["gadgets","photography"],char:"📷",fitzpatrick_scale:false,category:"objects"},camera_flash:{keywords:["photography","gadgets"],char:"📸",fitzpatrick_scale:false,category:"objects"},video_camera:{keywords:["film","record"],char:"📹",fitzpatrick_scale:false,category:"objects"},movie_camera:{keywords:["film","record"],char:"🎥",fitzpatrick_scale:false,category:"objects"},film_projector:{keywords:["video","tape","record","movie"],char:"📽",fitzpatrick_scale:false,category:"objects"},film_strip:{keywords:["movie"],char:"🎞",fitzpatrick_scale:false,category:"objects"},telephone_receiver:{keywords:["technology","communication","dial"],char:"📞",fitzpatrick_scale:false,category:"objects"},phone:{keywords:["technology","communication","dial","telephone"],char:"☎️",fitzpatrick_scale:false,category:"objects"},pager:{keywords:["bbcall","oldschool","90s"],char:"📟",fitzpatrick_scale:false,category:"objects"},fax:{keywords:["communication","technology"],char:"📠",fitzpatrick_scale:false,category:"objects"},tv:{keywords:["technology","program","oldschool","show","television"],char:"📺",fitzpatrick_scale:false,category:"objects"},radio:{keywords:["communication","music","podcast","program"],char:"📻",fitzpatrick_scale:false,category:"objects"},studio_microphone:{keywords:["sing","recording","artist","talkshow"],char:"🎙",fitzpatrick_scale:false,category:"objects"},level_slider:{keywords:["scale"],char:"🎚",fitzpatrick_scale:false,category:"objects"},control_knobs:{keywords:["dial"],char:"🎛",fitzpatrick_scale:false,category:"objects"},compass:{keywords:["magnetic","navigation","orienteering"],char:"🧭",fitzpatrick_scale:false,category:"objects"},stopwatch:{keywords:["time","deadline"],char:"⏱",fitzpatrick_scale:false,category:"objects"},timer_clock:{keywords:["alarm"],char:"⏲",fitzpatrick_scale:false,category:"objects"},alarm_clock:{keywords:["time","wake"],char:"⏰",fitzpatrick_scale:false,category:"objects"},mantelpiece_clock:{keywords:["time"],char:"🕰",fitzpatrick_scale:false,category:"objects"},hourglass_flowing_sand:{keywords:["oldschool","time","countdown"],char:"⏳",fitzpatrick_scale:false,category:"objects"},hourglass:{keywords:["time","clock","oldschool","limit","exam","quiz","test"],char:"⌛",fitzpatrick_scale:false,category:"objects"},satellite:{keywords:["communication","future","radio","space"],char:"📡",fitzpatrick_scale:false,category:"objects"},battery:{keywords:["power","energy","sustain"],char:"🔋",fitzpatrick_scale:false,category:"objects"},electric_plug:{keywords:["charger","power"],char:"🔌",fitzpatrick_scale:false,category:"objects"},bulb:{keywords:["light","electricity","idea"],char:"💡",fitzpatrick_scale:false,category:"objects"},flashlight:{keywords:["dark","camping","sight","night"],char:"🔦",fitzpatrick_scale:false,category:"objects"},candle:{keywords:["fire","wax"],char:"🕯",fitzpatrick_scale:false,category:"objects"},fire_extinguisher:{keywords:["quench"],char:"🧯",fitzpatrick_scale:false,category:"objects"},wastebasket:{keywords:["bin","trash","rubbish","garbage","toss"],char:"🗑",fitzpatrick_scale:false,category:"objects"},oil_drum:{keywords:["barrell"],char:"🛢",fitzpatrick_scale:false,category:"objects"},money_with_wings:{keywords:["dollar","bills","payment","sale"],char:"💸",fitzpatrick_scale:false,category:"objects"},dollar:{keywords:["money","sales","bill","currency"],char:"💵",fitzpatrick_scale:false,category:"objects"},yen:{keywords:["money","sales","japanese","dollar","currency"],char:"💴",fitzpatrick_scale:false,category:"objects"},euro:{keywords:["money","sales","dollar","currency"],char:"💶",fitzpatrick_scale:false,category:"objects"},pound:{keywords:["british","sterling","money","sales","bills","uk","england","currency"],char:"💷",fitzpatrick_scale:false,category:"objects"},moneybag:{keywords:["dollar","payment","coins","sale"],char:"💰",fitzpatrick_scale:false,category:"objects"},credit_card:{keywords:["money","sales","dollar","bill","payment","shopping"],char:"💳",fitzpatrick_scale:false,category:"objects"},gem:{keywords:["blue","ruby","diamond","jewelry"],char:"💎",fitzpatrick_scale:false,category:"objects"},balance_scale:{keywords:["law","fairness","weight"],char:"⚖",fitzpatrick_scale:false,category:"objects"},toolbox:{keywords:["tools","diy","fix","maintainer","mechanic"],char:"🧰",fitzpatrick_scale:false,category:"objects"},wrench:{keywords:["tools","diy","ikea","fix","maintainer"],char:"🔧",fitzpatrick_scale:false,category:"objects"},hammer:{keywords:["tools","build","create"],char:"🔨",fitzpatrick_scale:false,category:"objects"},hammer_and_pick:{keywords:["tools","build","create"],char:"⚒",fitzpatrick_scale:false,category:"objects"},hammer_and_wrench:{keywords:["tools","build","create"],char:"🛠",fitzpatrick_scale:false,category:"objects"},pick:{keywords:["tools","dig"],char:"⛏",fitzpatrick_scale:false,category:"objects"},nut_and_bolt:{keywords:["handy","tools","fix"],char:"🔩",fitzpatrick_scale:false,category:"objects"},gear:{keywords:["cog"],char:"⚙",fitzpatrick_scale:false,category:"objects"},brick:{keywords:["bricks"],char:"🧱",fitzpatrick_scale:false,category:"objects"},chains:{keywords:["lock","arrest"],char:"⛓",fitzpatrick_scale:false,category:"objects"},magnet:{keywords:["attraction","magnetic"],char:"🧲",fitzpatrick_scale:false,category:"objects"},gun:{keywords:["violence","weapon","pistol","revolver"],char:"🔫",fitzpatrick_scale:false,category:"objects"},bomb:{keywords:["boom","explode","explosion","terrorism"],char:"💣",fitzpatrick_scale:false,category:"objects"},firecracker:{keywords:["dynamite","boom","explode","explosion","explosive"],char:"🧨",fitzpatrick_scale:false,category:"objects"},hocho:{keywords:["knife","blade","cutlery","kitchen","weapon"],char:"🔪",fitzpatrick_scale:false,category:"objects"},dagger:{keywords:["weapon"],char:"🗡",fitzpatrick_scale:false,category:"objects"},crossed_swords:{keywords:["weapon"],char:"⚔",fitzpatrick_scale:false,category:"objects"},shield:{keywords:["protection","security"],char:"🛡",fitzpatrick_scale:false,category:"objects"},smoking:{keywords:["kills","tobacco","cigarette","joint","smoke"],char:"🚬",fitzpatrick_scale:false,category:"objects"},skull_and_crossbones:{keywords:["poison","danger","deadly","scary","death","pirate","evil"],char:"☠",fitzpatrick_scale:false,category:"objects"},coffin:{keywords:["vampire","dead","die","death","rip","graveyard","cemetery","casket","funeral","box"],char:"⚰",fitzpatrick_scale:false,category:"objects"},funeral_urn:{keywords:["dead","die","death","rip","ashes"],char:"⚱",fitzpatrick_scale:false,category:"objects"},amphora:{keywords:["vase","jar"],char:"🏺",fitzpatrick_scale:false,category:"objects"},crystal_ball:{keywords:["disco","party","magic","circus","fortune_teller"],char:"🔮",fitzpatrick_scale:false,category:"objects"},prayer_beads:{keywords:["dhikr","religious"],char:"📿",fitzpatrick_scale:false,category:"objects"},nazar_amulet:{keywords:["bead","charm"],char:"🧿",fitzpatrick_scale:false,category:"objects"},barber:{keywords:["hair","salon","style"],char:"💈",fitzpatrick_scale:false,category:"objects"},alembic:{keywords:["distilling","science","experiment","chemistry"],char:"⚗",fitzpatrick_scale:false,category:"objects"},telescope:{keywords:["stars","space","zoom","science","astronomy"],char:"🔭",fitzpatrick_scale:false,category:"objects"},microscope:{keywords:["laboratory","experiment","zoomin","science","study"],char:"🔬",fitzpatrick_scale:false,category:"objects"},hole:{keywords:["embarrassing"],char:"🕳",fitzpatrick_scale:false,category:"objects"},pill:{keywords:["health","medicine","doctor","pharmacy","drug"],char:"💊",fitzpatrick_scale:false,category:"objects"},syringe:{keywords:["health","hospital","drugs","blood","medicine","needle","doctor","nurse"],char:"💉",fitzpatrick_scale:false,category:"objects"},dna:{keywords:["biologist","genetics","life"],char:"🧬",fitzpatrick_scale:false,category:"objects"},microbe:{keywords:["amoeba","bacteria","germs"],char:"🦠",fitzpatrick_scale:false,category:"objects"},petri_dish:{keywords:["bacteria","biology","culture","lab"],char:"🧫",fitzpatrick_scale:false,category:"objects"},test_tube:{keywords:["chemistry","experiment","lab","science"],char:"🧪",fitzpatrick_scale:false,category:"objects"},thermometer:{keywords:["weather","temperature","hot","cold"],char:"🌡",fitzpatrick_scale:false,category:"objects"},broom:{keywords:["cleaning","sweeping","witch"],char:"🧹",fitzpatrick_scale:false,category:"objects"},basket:{keywords:["laundry"],char:"🧺",fitzpatrick_scale:false,category:"objects"},toilet_paper:{keywords:["roll"],char:"🧻",fitzpatrick_scale:false,category:"objects"},label:{keywords:["sale","tag"],char:"🏷",fitzpatrick_scale:false,category:"objects"},bookmark:{keywords:["favorite","label","save"],char:"🔖",fitzpatrick_scale:false,category:"objects"},toilet:{keywords:["restroom","wc","washroom","bathroom","potty"],char:"🚽",fitzpatrick_scale:false,category:"objects"},shower:{keywords:["clean","water","bathroom"],char:"🚿",fitzpatrick_scale:false,category:"objects"},bathtub:{keywords:["clean","shower","bathroom"],char:"🛁",fitzpatrick_scale:false,category:"objects"},soap:{keywords:["bar","bathing","cleaning","lather"],char:"🧼",fitzpatrick_scale:false,category:"objects"},sponge:{keywords:["absorbing","cleaning","porous"],char:"🧽",fitzpatrick_scale:false,category:"objects"},lotion_bottle:{keywords:["moisturizer","sunscreen"],char:"🧴",fitzpatrick_scale:false,category:"objects"},key:{keywords:["lock","door","password"],char:"🔑",fitzpatrick_scale:false,category:"objects"},old_key:{keywords:["lock","door","password"],char:"🗝",fitzpatrick_scale:false,category:"objects"},couch_and_lamp:{keywords:["read","chill"],char:"🛋",fitzpatrick_scale:false,category:"objects"},sleeping_bed:{keywords:["bed","rest"],char:"🛌",fitzpatrick_scale:true,category:"objects"},bed:{keywords:["sleep","rest"],char:"🛏",fitzpatrick_scale:false,category:"objects"},door:{keywords:["house","entry","exit"],char:"🚪",fitzpatrick_scale:false,category:"objects"},bellhop_bell:{keywords:["service"],char:"🛎",fitzpatrick_scale:false,category:"objects"},teddy_bear:{keywords:["plush","stuffed"],char:"🧸",fitzpatrick_scale:false,category:"objects"},framed_picture:{keywords:["photography"],char:"🖼",fitzpatrick_scale:false,category:"objects"},world_map:{keywords:["location","direction"],char:"🗺",fitzpatrick_scale:false,category:"objects"},parasol_on_ground:{keywords:["weather","summer"],char:"⛱",fitzpatrick_scale:false,category:"objects"},moyai:{keywords:["rock","easter island","moai"],char:"🗿",fitzpatrick_scale:false,category:"objects"},shopping:{keywords:["mall","buy","purchase"],char:"🛍",fitzpatrick_scale:false,category:"objects"},shopping_cart:{keywords:["trolley"],char:"🛒",fitzpatrick_scale:false,category:"objects"},balloon:{keywords:["party","celebration","birthday","circus"],char:"🎈",fitzpatrick_scale:false,category:"objects"},flags:{keywords:["fish","japanese","koinobori","carp","banner"],char:"🎏",fitzpatrick_scale:false,category:"objects"},ribbon:{keywords:["decoration","pink","girl","bowtie"],char:"🎀",fitzpatrick_scale:false,category:"objects"},gift:{keywords:["present","birthday","christmas","xmas"],char:"🎁",fitzpatrick_scale:false,category:"objects"},confetti_ball:{keywords:["festival","party","birthday","circus"],char:"🎊",fitzpatrick_scale:false,category:"objects"},tada:{keywords:["party","congratulations","birthday","magic","circus","celebration"],char:"🎉",fitzpatrick_scale:false,category:"objects"},dolls:{keywords:["japanese","toy","kimono"],char:"🎎",fitzpatrick_scale:false,category:"objects"},wind_chime:{keywords:["nature","ding","spring","bell"],char:"🎐",fitzpatrick_scale:false,category:"objects"},crossed_flags:{keywords:["japanese","nation","country","border"],char:"🎌",fitzpatrick_scale:false,category:"objects"},izakaya_lantern:{keywords:["light","paper","halloween","spooky"],char:"🏮",fitzpatrick_scale:false,category:"objects"},red_envelope:{keywords:["gift"],char:"🧧",fitzpatrick_scale:false,category:"objects"},email:{keywords:["letter","postal","inbox","communication"],char:"✉️",fitzpatrick_scale:false,category:"objects"},envelope_with_arrow:{keywords:["email","communication"],char:"📩",fitzpatrick_scale:false,category:"objects"},incoming_envelope:{keywords:["email","inbox"],char:"📨",fitzpatrick_scale:false,category:"objects"},"e-mail":{keywords:["communication","inbox"],char:"📧",fitzpatrick_scale:false,category:"objects"},love_letter:{keywords:["email","like","affection","envelope","valentines"],char:"💌",fitzpatrick_scale:false,category:"objects"},postbox:{keywords:["email","letter","envelope"],char:"📮",fitzpatrick_scale:false,category:"objects"},mailbox_closed:{keywords:["email","communication","inbox"],char:"📪",fitzpatrick_scale:false,category:"objects"},mailbox:{keywords:["email","inbox","communication"],char:"📫",fitzpatrick_scale:false,category:"objects"},mailbox_with_mail:{keywords:["email","inbox","communication"],char:"📬",fitzpatrick_scale:false,category:"objects"},mailbox_with_no_mail:{keywords:["email","inbox"],char:"📭",fitzpatrick_scale:false,category:"objects"},package:{keywords:["mail","gift","cardboard","box","moving"],char:"📦",fitzpatrick_scale:false,category:"objects"},postal_horn:{keywords:["instrument","music"],char:"📯",fitzpatrick_scale:false,category:"objects"},inbox_tray:{keywords:["email","documents"],char:"📥",fitzpatrick_scale:false,category:"objects"},outbox_tray:{keywords:["inbox","email"],char:"📤",fitzpatrick_scale:false,category:"objects"},scroll:{keywords:["documents","ancient","history","paper"],char:"📜",fitzpatrick_scale:false,category:"objects"},page_with_curl:{keywords:["documents","office","paper"],char:"📃",fitzpatrick_scale:false,category:"objects"},bookmark_tabs:{keywords:["favorite","save","order","tidy"],char:"📑",fitzpatrick_scale:false,category:"objects"},receipt:{keywords:["accounting","expenses"],char:"🧾",fitzpatrick_scale:false,category:"objects"},bar_chart:{keywords:["graph","presentation","stats"],char:"📊",fitzpatrick_scale:false,category:"objects"},chart_with_upwards_trend:{keywords:["graph","presentation","stats","recovery","business","economics","money","sales","good","success"],char:"📈",fitzpatrick_scale:false,category:"objects"},chart_with_downwards_trend:{keywords:["graph","presentation","stats","recession","business","economics","money","sales","bad","failure"],char:"📉",fitzpatrick_scale:false,category:"objects"},page_facing_up:{keywords:["documents","office","paper","information"],char:"📄",fitzpatrick_scale:false,category:"objects"},date:{keywords:["calendar","schedule"],char:"📅",fitzpatrick_scale:false,category:"objects"},calendar:{keywords:["schedule","date","planning"],char:"📆",fitzpatrick_scale:false,category:"objects"},spiral_calendar:{keywords:["date","schedule","planning"],char:"🗓",fitzpatrick_scale:false,category:"objects"},card_index:{keywords:["business","stationery"],char:"📇",fitzpatrick_scale:false,category:"objects"},card_file_box:{keywords:["business","stationery"],char:"🗃",fitzpatrick_scale:false,category:"objects"},ballot_box:{keywords:["election","vote"],char:"🗳",fitzpatrick_scale:false,category:"objects"},file_cabinet:{keywords:["filing","organizing"],char:"🗄",fitzpatrick_scale:false,category:"objects"},clipboard:{keywords:["stationery","documents"],char:"📋",fitzpatrick_scale:false,category:"objects"},spiral_notepad:{keywords:["memo","stationery"],char:"🗒",fitzpatrick_scale:false,category:"objects"},file_folder:{keywords:["documents","business","office"],char:"📁",fitzpatrick_scale:false,category:"objects"},open_file_folder:{keywords:["documents","load"],char:"📂",fitzpatrick_scale:false,category:"objects"},card_index_dividers:{keywords:["organizing","business","stationery"],char:"🗂",fitzpatrick_scale:false,category:"objects"},newspaper_roll:{keywords:["press","headline"],char:"🗞",fitzpatrick_scale:false,category:"objects"},newspaper:{keywords:["press","headline"],char:"📰",fitzpatrick_scale:false,category:"objects"},notebook:{keywords:["stationery","record","notes","paper","study"],char:"📓",fitzpatrick_scale:false,category:"objects"},closed_book:{keywords:["read","library","knowledge","textbook","learn"],char:"📕",fitzpatrick_scale:false,category:"objects"},green_book:{keywords:["read","library","knowledge","study"],char:"📗",fitzpatrick_scale:false,category:"objects"},blue_book:{keywords:["read","library","knowledge","learn","study"],char:"📘",fitzpatrick_scale:false,category:"objects"},orange_book:{keywords:["read","library","knowledge","textbook","study"],char:"📙",fitzpatrick_scale:false,category:"objects"},notebook_with_decorative_cover:{keywords:["classroom","notes","record","paper","study"],char:"📔",fitzpatrick_scale:false,category:"objects"},ledger:{keywords:["notes","paper"],char:"📒",fitzpatrick_scale:false,category:"objects"},books:{keywords:["literature","library","study"],char:"📚",fitzpatrick_scale:false,category:"objects"},open_book:{keywords:["book","read","library","knowledge","literature","learn","study"],char:"📖",fitzpatrick_scale:false,category:"objects"},safety_pin:{keywords:["diaper"],char:"🧷",fitzpatrick_scale:false,category:"objects"},link:{keywords:["rings","url"],char:"🔗",fitzpatrick_scale:false,category:"objects"},paperclip:{keywords:["documents","stationery"],char:"📎",fitzpatrick_scale:false,category:"objects"},paperclips:{keywords:["documents","stationery"],char:"🖇",fitzpatrick_scale:false,category:"objects"},scissors:{keywords:["stationery","cut"],char:"✂️",fitzpatrick_scale:false,category:"objects"},triangular_ruler:{keywords:["stationery","math","architect","sketch"],char:"📐",fitzpatrick_scale:false,category:"objects"},straight_ruler:{keywords:["stationery","calculate","length","math","school","drawing","architect","sketch"],char:"📏",fitzpatrick_scale:false,category:"objects"},abacus:{keywords:["calculation"],char:"🧮",fitzpatrick_scale:false,category:"objects"},pushpin:{keywords:["stationery","mark","here"],char:"📌",fitzpatrick_scale:false,category:"objects"},round_pushpin:{keywords:["stationery","location","map","here"],char:"📍",fitzpatrick_scale:false,category:"objects"},triangular_flag_on_post:{keywords:["mark","milestone","place"],char:"🚩",fitzpatrick_scale:false,category:"objects"},white_flag:{keywords:["losing","loser","lost","surrender","give up","fail"],char:"🏳",fitzpatrick_scale:false,category:"objects"},black_flag:{keywords:["pirate"],char:"🏴",fitzpatrick_scale:false,category:"objects"},rainbow_flag:{keywords:["flag","rainbow","pride","gay","lgbt","glbt","queer","homosexual","lesbian","bisexual","transgender"],char:"🏳️‍🌈",fitzpatrick_scale:false,category:"objects"},closed_lock_with_key:{keywords:["security","privacy"],char:"🔐",fitzpatrick_scale:false,category:"objects"},lock:{keywords:["security","password","padlock"],char:"🔒",fitzpatrick_scale:false,category:"objects"},unlock:{keywords:["privacy","security"],char:"🔓",fitzpatrick_scale:false,category:"objects"},lock_with_ink_pen:{keywords:["security","secret"],char:"🔏",fitzpatrick_scale:false,category:"objects"},pen:{keywords:["stationery","writing","write"],char:"🖊",fitzpatrick_scale:false,category:"objects"},fountain_pen:{keywords:["stationery","writing","write"],char:"🖋",fitzpatrick_scale:false,category:"objects"},black_nib:{keywords:["pen","stationery","writing","write"],char:"✒️",fitzpatrick_scale:false,category:"objects"},memo:{keywords:["write","documents","stationery","pencil","paper","writing","legal","exam","quiz","test","study","compose"],char:"📝",fitzpatrick_scale:false,category:"objects"},pencil2:{keywords:["stationery","write","paper","writing","school","study"],char:"✏️",fitzpatrick_scale:false,category:"objects"},crayon:{keywords:["drawing","creativity"],char:"🖍",fitzpatrick_scale:false,category:"objects"},paintbrush:{keywords:["drawing","creativity","art"],char:"🖌",fitzpatrick_scale:false,category:"objects"},mag:{keywords:["search","zoom","find","detective"],char:"🔍",fitzpatrick_scale:false,category:"objects"},mag_right:{keywords:["search","zoom","find","detective"],char:"🔎",fitzpatrick_scale:false,category:"objects"},heart:{keywords:["love","like","valentines"],char:"❤️",fitzpatrick_scale:false,category:"symbols"},orange_heart:{keywords:["love","like","affection","valentines"],char:"🧡",fitzpatrick_scale:false,category:"symbols"},yellow_heart:{keywords:["love","like","affection","valentines"],char:"💛",fitzpatrick_scale:false,category:"symbols"},green_heart:{keywords:["love","like","affection","valentines"],char:"💚",fitzpatrick_scale:false,category:"symbols"},blue_heart:{keywords:["love","like","affection","valentines"],char:"💙",fitzpatrick_scale:false,category:"symbols"},purple_heart:{keywords:["love","like","affection","valentines"],char:"💜",fitzpatrick_scale:false,category:"symbols"},black_heart:{keywords:["evil"],char:"🖤",fitzpatrick_scale:false,category:"symbols"},broken_heart:{keywords:["sad","sorry","break","heart","heartbreak"],char:"💔",fitzpatrick_scale:false,category:"symbols"},heavy_heart_exclamation:{keywords:["decoration","love"],char:"❣",fitzpatrick_scale:false,category:"symbols"},two_hearts:{keywords:["love","like","affection","valentines","heart"],char:"💕",fitzpatrick_scale:false,category:"symbols"},revolving_hearts:{keywords:["love","like","affection","valentines"],char:"💞",fitzpatrick_scale:false,category:"symbols"},heartbeat:{keywords:["love","like","affection","valentines","pink","heart"],char:"💓",fitzpatrick_scale:false,category:"symbols"},heartpulse:{keywords:["like","love","affection","valentines","pink"],char:"💗",fitzpatrick_scale:false,category:"symbols"},sparkling_heart:{keywords:["love","like","affection","valentines"],char:"💖",fitzpatrick_scale:false,category:"symbols"},cupid:{keywords:["love","like","heart","affection","valentines"],char:"💘",fitzpatrick_scale:false,category:"symbols"},gift_heart:{keywords:["love","valentines"],char:"💝",fitzpatrick_scale:false,category:"symbols"},heart_decoration:{keywords:["purple-square","love","like"],char:"💟",fitzpatrick_scale:false,category:"symbols"},peace_symbol:{keywords:["hippie"],char:"☮",fitzpatrick_scale:false,category:"symbols"},latin_cross:{keywords:["christianity"],char:"✝",fitzpatrick_scale:false,category:"symbols"},star_and_crescent:{keywords:["islam"],char:"☪",fitzpatrick_scale:false,category:"symbols"},om:{keywords:["hinduism","buddhism","sikhism","jainism"],char:"🕉",fitzpatrick_scale:false,category:"symbols"},wheel_of_dharma:{keywords:["hinduism","buddhism","sikhism","jainism"],char:"☸",fitzpatrick_scale:false,category:"symbols"},star_of_david:{keywords:["judaism"],char:"✡",fitzpatrick_scale:false,category:"symbols"},six_pointed_star:{keywords:["purple-square","religion","jewish","hexagram"],char:"🔯",fitzpatrick_scale:false,category:"symbols"},menorah:{keywords:["hanukkah","candles","jewish"],char:"🕎",fitzpatrick_scale:false,category:"symbols"},yin_yang:{keywords:["balance"],char:"☯",fitzpatrick_scale:false,category:"symbols"},orthodox_cross:{keywords:["suppedaneum","religion"],char:"☦",fitzpatrick_scale:false,category:"symbols"},place_of_worship:{keywords:["religion","church","temple","prayer"],char:"🛐",fitzpatrick_scale:false,category:"symbols"},ophiuchus:{keywords:["sign","purple-square","constellation","astrology"],char:"⛎",fitzpatrick_scale:false,category:"symbols"},aries:{keywords:["sign","purple-square","zodiac","astrology"],char:"♈",fitzpatrick_scale:false,category:"symbols"},taurus:{keywords:["purple-square","sign","zodiac","astrology"],char:"♉",fitzpatrick_scale:false,category:"symbols"},gemini:{keywords:["sign","zodiac","purple-square","astrology"],char:"♊",fitzpatrick_scale:false,category:"symbols"},cancer:{keywords:["sign","zodiac","purple-square","astrology"],char:"♋",fitzpatrick_scale:false,category:"symbols"},leo:{keywords:["sign","purple-square","zodiac","astrology"],char:"♌",fitzpatrick_scale:false,category:"symbols"},virgo:{keywords:["sign","zodiac","purple-square","astrology"],char:"♍",fitzpatrick_scale:false,category:"symbols"},libra:{keywords:["sign","purple-square","zodiac","astrology"],char:"♎",fitzpatrick_scale:false,category:"symbols"},scorpius:{keywords:["sign","zodiac","purple-square","astrology","scorpio"],char:"♏",fitzpatrick_scale:false,category:"symbols"},sagittarius:{keywords:["sign","zodiac","purple-square","astrology"],char:"♐",fitzpatrick_scale:false,category:"symbols"},capricorn:{keywords:["sign","zodiac","purple-square","astrology"],char:"♑",fitzpatrick_scale:false,category:"symbols"},aquarius:{keywords:["sign","purple-square","zodiac","astrology"],char:"♒",fitzpatrick_scale:false,category:"symbols"},pisces:{keywords:["purple-square","sign","zodiac","astrology"],char:"♓",fitzpatrick_scale:false,category:"symbols"},id:{keywords:["purple-square","words"],char:"🆔",fitzpatrick_scale:false,category:"symbols"},atom_symbol:{keywords:["science","physics","chemistry"],char:"⚛",fitzpatrick_scale:false,category:"symbols"},u7a7a:{keywords:["kanji","japanese","chinese","empty","sky","blue-square"],char:"🈳",fitzpatrick_scale:false,category:"symbols"},u5272:{keywords:["cut","divide","chinese","kanji","pink-square"],char:"🈹",fitzpatrick_scale:false,category:"symbols"},radioactive:{keywords:["nuclear","danger"],char:"☢",fitzpatrick_scale:false,category:"symbols"},biohazard:{keywords:["danger"],char:"☣",fitzpatrick_scale:false,category:"symbols"},mobile_phone_off:{keywords:["mute","orange-square","silence","quiet"],char:"📴",fitzpatrick_scale:false,category:"symbols"},vibration_mode:{keywords:["orange-square","phone"],char:"📳",fitzpatrick_scale:false,category:"symbols"},u6709:{keywords:["orange-square","chinese","have","kanji"],char:"🈶",fitzpatrick_scale:false,category:"symbols"},u7121:{keywords:["nothing","chinese","kanji","japanese","orange-square"],char:"🈚",fitzpatrick_scale:false,category:"symbols"},u7533:{keywords:["chinese","japanese","kanji","orange-square"],char:"🈸",fitzpatrick_scale:false,category:"symbols"},u55b6:{keywords:["japanese","opening hours","orange-square"],char:"🈺",fitzpatrick_scale:false,category:"symbols"},u6708:{keywords:["chinese","month","moon","japanese","orange-square","kanji"],char:"🈷️",fitzpatrick_scale:false,category:"symbols"},eight_pointed_black_star:{keywords:["orange-square","shape","polygon"],char:"✴️",fitzpatrick_scale:false,category:"symbols"},vs:{keywords:["words","orange-square"],char:"🆚",fitzpatrick_scale:false,category:"symbols"},accept:{keywords:["ok","good","chinese","kanji","agree","yes","orange-circle"],char:"🉑",fitzpatrick_scale:false,category:"symbols"},white_flower:{keywords:["japanese","spring"],char:"💮",fitzpatrick_scale:false,category:"symbols"},ideograph_advantage:{keywords:["chinese","kanji","obtain","get","circle"],char:"🉐",fitzpatrick_scale:false,category:"symbols"},secret:{keywords:["privacy","chinese","sshh","kanji","red-circle"],char:"㊙️",fitzpatrick_scale:false,category:"symbols"},congratulations:{keywords:["chinese","kanji","japanese","red-circle"],char:"㊗️",fitzpatrick_scale:false,category:"symbols"},u5408:{keywords:["japanese","chinese","join","kanji","red-square"],char:"🈴",fitzpatrick_scale:false,category:"symbols"},u6e80:{keywords:["full","chinese","japanese","red-square","kanji"],char:"🈵",fitzpatrick_scale:false,category:"symbols"},u7981:{keywords:["kanji","japanese","chinese","forbidden","limit","restricted","red-square"],char:"🈲",fitzpatrick_scale:false,category:"symbols"},a:{keywords:["red-square","alphabet","letter"],char:"🅰️",fitzpatrick_scale:false,category:"symbols"},b:{keywords:["red-square","alphabet","letter"],char:"🅱️",fitzpatrick_scale:false,category:"symbols"},ab:{keywords:["red-square","alphabet"],char:"🆎",fitzpatrick_scale:false,category:"symbols"},cl:{keywords:["alphabet","words","red-square"],char:"🆑",fitzpatrick_scale:false,category:"symbols"},o2:{keywords:["alphabet","red-square","letter"],char:"🅾️",fitzpatrick_scale:false,category:"symbols"},sos:{keywords:["help","red-square","words","emergency","911"],char:"🆘",fitzpatrick_scale:false,category:"symbols"},no_entry:{keywords:["limit","security","privacy","bad","denied","stop","circle"],char:"⛔",fitzpatrick_scale:false,category:"symbols"},name_badge:{keywords:["fire","forbid"],char:"📛",fitzpatrick_scale:false,category:"symbols"},no_entry_sign:{keywords:["forbid","stop","limit","denied","disallow","circle"],char:"🚫",fitzpatrick_scale:false,category:"symbols"},x:{keywords:["no","delete","remove","cancel","red"],char:"❌",fitzpatrick_scale:false,category:"symbols"},o:{keywords:["circle","round"],char:"⭕",fitzpatrick_scale:false,category:"symbols"},stop_sign:{keywords:["stop"],char:"🛑",fitzpatrick_scale:false,category:"symbols"},anger:{keywords:["angry","mad"],char:"💢",fitzpatrick_scale:false,category:"symbols"},hotsprings:{keywords:["bath","warm","relax"],char:"♨️",fitzpatrick_scale:false,category:"symbols"},no_pedestrians:{keywords:["rules","crossing","walking","circle"],char:"🚷",fitzpatrick_scale:false,category:"symbols"},do_not_litter:{keywords:["trash","bin","garbage","circle"],char:"🚯",fitzpatrick_scale:false,category:"symbols"},no_bicycles:{keywords:["cyclist","prohibited","circle"],char:"🚳",fitzpatrick_scale:false,category:"symbols"},"non-potable_water":{keywords:["drink","faucet","tap","circle"],char:"🚱",fitzpatrick_scale:false,category:"symbols"},underage:{keywords:["18","drink","pub","night","minor","circle"],char:"🔞",fitzpatrick_scale:false,category:"symbols"},no_mobile_phones:{keywords:["iphone","mute","circle"],char:"📵",fitzpatrick_scale:false,category:"symbols"},exclamation:{keywords:["heavy_exclamation_mark","danger","surprise","punctuation","wow","warning"],char:"❗",fitzpatrick_scale:false,category:"symbols"},grey_exclamation:{keywords:["surprise","punctuation","gray","wow","warning"],char:"❕",fitzpatrick_scale:false,category:"symbols"},question:{keywords:["doubt","confused"],char:"❓",fitzpatrick_scale:false,category:"symbols"},grey_question:{keywords:["doubts","gray","huh","confused"],char:"❔",fitzpatrick_scale:false,category:"symbols"},bangbang:{keywords:["exclamation","surprise"],char:"‼️",fitzpatrick_scale:false,category:"symbols"},interrobang:{keywords:["wat","punctuation","surprise"],char:"⁉️",fitzpatrick_scale:false,category:"symbols"},100:{keywords:["score","perfect","numbers","century","exam","quiz","test","pass","hundred"],char:"💯",fitzpatrick_scale:false,category:"symbols"},low_brightness:{keywords:["sun","afternoon","warm","summer"],char:"🔅",fitzpatrick_scale:false,category:"symbols"},high_brightness:{keywords:["sun","light"],char:"🔆",fitzpatrick_scale:false,category:"symbols"},trident:{keywords:["weapon","spear"],char:"🔱",fitzpatrick_scale:false,category:"symbols"},fleur_de_lis:{keywords:["decorative","scout"],char:"⚜",fitzpatrick_scale:false,category:"symbols"},part_alternation_mark:{keywords:["graph","presentation","stats","business","economics","bad"],char:"〽️",fitzpatrick_scale:false,category:"symbols"},warning:{keywords:["exclamation","wip","alert","error","problem","issue"],char:"⚠️",fitzpatrick_scale:false,category:"symbols"},children_crossing:{keywords:["school","warning","danger","sign","driving","yellow-diamond"],char:"🚸",fitzpatrick_scale:false,category:"symbols"},beginner:{keywords:["badge","shield"],char:"🔰",fitzpatrick_scale:false,category:"symbols"},recycle:{keywords:["arrow","environment","garbage","trash"],char:"♻️",fitzpatrick_scale:false,category:"symbols"},u6307:{keywords:["chinese","point","green-square","kanji"],char:"🈯",fitzpatrick_scale:false,category:"symbols"},chart:{keywords:["green-square","graph","presentation","stats"],char:"💹",fitzpatrick_scale:false,category:"symbols"},sparkle:{keywords:["stars","green-square","awesome","good","fireworks"],char:"❇️",fitzpatrick_scale:false,category:"symbols"},eight_spoked_asterisk:{keywords:["star","sparkle","green-square"],char:"✳️",fitzpatrick_scale:false,category:"symbols"},negative_squared_cross_mark:{keywords:["x","green-square","no","deny"],char:"❎",fitzpatrick_scale:false,category:"symbols"},white_check_mark:{keywords:["green-square","ok","agree","vote","election","answer","tick"],char:"✅",fitzpatrick_scale:false,category:"symbols"},diamond_shape_with_a_dot_inside:{keywords:["jewel","blue","gem","crystal","fancy"],char:"💠",fitzpatrick_scale:false,category:"symbols"},cyclone:{keywords:["weather","swirl","blue","cloud","vortex","spiral","whirlpool","spin","tornado","hurricane","typhoon"],char:"🌀",fitzpatrick_scale:false,category:"symbols"},loop:{keywords:["tape","cassette"],char:"➿",fitzpatrick_scale:false,category:"symbols"},globe_with_meridians:{keywords:["earth","international","world","internet","interweb","i18n"],char:"🌐",fitzpatrick_scale:false,category:"symbols"},m:{keywords:["alphabet","blue-circle","letter"],char:"Ⓜ️",fitzpatrick_scale:false,category:"symbols"},atm:{keywords:["money","sales","cash","blue-square","payment","bank"],char:"🏧",fitzpatrick_scale:false,category:"symbols"},sa:{keywords:["japanese","blue-square","katakana"],char:"🈂️",fitzpatrick_scale:false,category:"symbols"},passport_control:{keywords:["custom","blue-square"],char:"🛂",fitzpatrick_scale:false,category:"symbols"},customs:{keywords:["passport","border","blue-square"],char:"🛃",fitzpatrick_scale:false,category:"symbols"},baggage_claim:{keywords:["blue-square","airport","transport"],char:"🛄",fitzpatrick_scale:false,category:"symbols"},left_luggage:{keywords:["blue-square","travel"],char:"🛅",fitzpatrick_scale:false,category:"symbols"},wheelchair:{keywords:["blue-square","disabled","a11y","accessibility"],char:"♿",fitzpatrick_scale:false,category:"symbols"},no_smoking:{keywords:["cigarette","blue-square","smell","smoke"],char:"🚭",fitzpatrick_scale:false,category:"symbols"},wc:{keywords:["toilet","restroom","blue-square"],char:"🚾",fitzpatrick_scale:false,category:"symbols"},parking:{keywords:["cars","blue-square","alphabet","letter"],char:"🅿️",fitzpatrick_scale:false,category:"symbols"},potable_water:{keywords:["blue-square","liquid","restroom","cleaning","faucet"],char:"🚰",fitzpatrick_scale:false,category:"symbols"},mens:{keywords:["toilet","restroom","wc","blue-square","gender","male"],char:"🚹",fitzpatrick_scale:false,category:"symbols"},womens:{keywords:["purple-square","woman","female","toilet","loo","restroom","gender"],char:"🚺",fitzpatrick_scale:false,category:"symbols"},baby_symbol:{keywords:["orange-square","child"],char:"🚼",fitzpatrick_scale:false,category:"symbols"},restroom:{keywords:["blue-square","toilet","refresh","wc","gender"],char:"🚻",fitzpatrick_scale:false,category:"symbols"},put_litter_in_its_place:{keywords:["blue-square","sign","human","info"],char:"🚮",fitzpatrick_scale:false,category:"symbols"},cinema:{keywords:["blue-square","record","film","movie","curtain","stage","theater"],char:"🎦",fitzpatrick_scale:false,category:"symbols"},signal_strength:{keywords:["blue-square","reception","phone","internet","connection","wifi","bluetooth","bars"],char:"📶",fitzpatrick_scale:false,category:"symbols"},koko:{keywords:["blue-square","here","katakana","japanese","destination"],char:"🈁",fitzpatrick_scale:false,category:"symbols"},ng:{keywords:["blue-square","words","shape","icon"],char:"🆖",fitzpatrick_scale:false,category:"symbols"},ok:{keywords:["good","agree","yes","blue-square"],char:"🆗",fitzpatrick_scale:false,category:"symbols"},up:{keywords:["blue-square","above","high"],char:"🆙",fitzpatrick_scale:false,category:"symbols"},cool:{keywords:["words","blue-square"],char:"🆒",fitzpatrick_scale:false,category:"symbols"},new:{keywords:["blue-square","words","start"],char:"🆕",fitzpatrick_scale:false,category:"symbols"},free:{keywords:["blue-square","words"],char:"🆓",fitzpatrick_scale:false,category:"symbols"},zero:{keywords:["0","numbers","blue-square","null"],char:"0️⃣",fitzpatrick_scale:false,category:"symbols"},one:{keywords:["blue-square","numbers","1"],char:"1️⃣",fitzpatrick_scale:false,category:"symbols"},two:{keywords:["numbers","2","prime","blue-square"],char:"2️⃣",fitzpatrick_scale:false,category:"symbols"},three:{keywords:["3","numbers","prime","blue-square"],char:"3️⃣",fitzpatrick_scale:false,category:"symbols"},four:{keywords:["4","numbers","blue-square"],char:"4️⃣",fitzpatrick_scale:false,category:"symbols"},five:{keywords:["5","numbers","blue-square","prime"],char:"5️⃣",fitzpatrick_scale:false,category:"symbols"},six:{keywords:["6","numbers","blue-square"],char:"6️⃣",fitzpatrick_scale:false,category:"symbols"},seven:{keywords:["7","numbers","blue-square","prime"],char:"7️⃣",fitzpatrick_scale:false,category:"symbols"},eight:{keywords:["8","blue-square","numbers"],char:"8️⃣",fitzpatrick_scale:false,category:"symbols"},nine:{keywords:["blue-square","numbers","9"],char:"9️⃣",fitzpatrick_scale:false,category:"symbols"},keycap_ten:{keywords:["numbers","10","blue-square"],char:"🔟",fitzpatrick_scale:false,category:"symbols"},asterisk:{keywords:["star","keycap"],char:"*⃣",fitzpatrick_scale:false,category:"symbols"},1234:{keywords:["numbers","blue-square"],char:"🔢",fitzpatrick_scale:false,category:"symbols"},eject_button:{keywords:["blue-square"],char:"⏏️",fitzpatrick_scale:false,category:"symbols"},arrow_forward:{keywords:["blue-square","right","direction","play"],char:"▶️",fitzpatrick_scale:false,category:"symbols"},pause_button:{keywords:["pause","blue-square"],char:"⏸",fitzpatrick_scale:false,category:"symbols"},next_track_button:{keywords:["forward","next","blue-square"],char:"⏭",fitzpatrick_scale:false,category:"symbols"},stop_button:{keywords:["blue-square"],char:"⏹",fitzpatrick_scale:false,category:"symbols"},record_button:{keywords:["blue-square"],char:"⏺",fitzpatrick_scale:false,category:"symbols"},play_or_pause_button:{keywords:["blue-square","play","pause"],char:"⏯",fitzpatrick_scale:false,category:"symbols"},previous_track_button:{keywords:["backward"],char:"⏮",fitzpatrick_scale:false,category:"symbols"},fast_forward:{keywords:["blue-square","play","speed","continue"],char:"⏩",fitzpatrick_scale:false,category:"symbols"},rewind:{keywords:["play","blue-square"],char:"⏪",fitzpatrick_scale:false,category:"symbols"},twisted_rightwards_arrows:{keywords:["blue-square","shuffle","music","random"],char:"🔀",fitzpatrick_scale:false,category:"symbols"},repeat:{keywords:["loop","record"],char:"🔁",fitzpatrick_scale:false,category:"symbols"},repeat_one:{keywords:["blue-square","loop"],char:"🔂",fitzpatrick_scale:false,category:"symbols"},arrow_backward:{keywords:["blue-square","left","direction"],char:"◀️",fitzpatrick_scale:false,category:"symbols"},arrow_up_small:{keywords:["blue-square","triangle","direction","point","forward","top"],char:"🔼",fitzpatrick_scale:false,category:"symbols"},arrow_down_small:{keywords:["blue-square","direction","bottom"],char:"🔽",fitzpatrick_scale:false,category:"symbols"},arrow_double_up:{keywords:["blue-square","direction","top"],char:"⏫",fitzpatrick_scale:false,category:"symbols"},arrow_double_down:{keywords:["blue-square","direction","bottom"],char:"⏬",fitzpatrick_scale:false,category:"symbols"},arrow_right:{keywords:["blue-square","next"],char:"➡️",fitzpatrick_scale:false,category:"symbols"},arrow_left:{keywords:["blue-square","previous","back"],char:"⬅️",fitzpatrick_scale:false,category:"symbols"},arrow_up:{keywords:["blue-square","continue","top","direction"],char:"⬆️",fitzpatrick_scale:false,category:"symbols"},arrow_down:{keywords:["blue-square","direction","bottom"],char:"⬇️",fitzpatrick_scale:false,category:"symbols"},arrow_upper_right:{keywords:["blue-square","point","direction","diagonal","northeast"],char:"↗️",fitzpatrick_scale:false,category:"symbols"},arrow_lower_right:{keywords:["blue-square","direction","diagonal","southeast"],char:"↘️",fitzpatrick_scale:false,category:"symbols"},arrow_lower_left:{keywords:["blue-square","direction","diagonal","southwest"],char:"↙️",fitzpatrick_scale:false,category:"symbols"},arrow_upper_left:{keywords:["blue-square","point","direction","diagonal","northwest"],char:"↖️",fitzpatrick_scale:false,category:"symbols"},arrow_up_down:{keywords:["blue-square","direction","way","vertical"],char:"↕️",fitzpatrick_scale:false,category:"symbols"},left_right_arrow:{keywords:["shape","direction","horizontal","sideways"],char:"↔️",fitzpatrick_scale:false,category:"symbols"},arrows_counterclockwise:{keywords:["blue-square","sync","cycle"],char:"🔄",fitzpatrick_scale:false,category:"symbols"},arrow_right_hook:{keywords:["blue-square","return","rotate","direction"],char:"↪️",fitzpatrick_scale:false,category:"symbols"},leftwards_arrow_with_hook:{keywords:["back","return","blue-square","undo","enter"],char:"↩️",fitzpatrick_scale:false,category:"symbols"},arrow_heading_up:{keywords:["blue-square","direction","top"],char:"⤴️",fitzpatrick_scale:false,category:"symbols"},arrow_heading_down:{keywords:["blue-square","direction","bottom"],char:"⤵️",fitzpatrick_scale:false,category:"symbols"},hash:{keywords:["symbol","blue-square","twitter"],char:"#️⃣",fitzpatrick_scale:false,category:"symbols"},information_source:{keywords:["blue-square","alphabet","letter"],char:"ℹ️",fitzpatrick_scale:false,category:"symbols"},abc:{keywords:["blue-square","alphabet"],char:"🔤",fitzpatrick_scale:false,category:"symbols"},abcd:{keywords:["blue-square","alphabet"],char:"🔡",fitzpatrick_scale:false,category:"symbols"},capital_abcd:{keywords:["alphabet","words","blue-square"],char:"🔠",fitzpatrick_scale:false,category:"symbols"},symbols:{keywords:["blue-square","music","note","ampersand","percent","glyphs","characters"],char:"🔣",fitzpatrick_scale:false,category:"symbols"},musical_note:{keywords:["score","tone","sound"],char:"🎵",fitzpatrick_scale:false,category:"symbols"},notes:{keywords:["music","score"],char:"🎶",fitzpatrick_scale:false,category:"symbols"},wavy_dash:{keywords:["draw","line","moustache","mustache","squiggle","scribble"],char:"〰️",fitzpatrick_scale:false,category:"symbols"},curly_loop:{keywords:["scribble","draw","shape","squiggle"],char:"➰",fitzpatrick_scale:false,category:"symbols"},heavy_check_mark:{keywords:["ok","nike","answer","yes","tick"],char:"✔️",fitzpatrick_scale:false,category:"symbols"},arrows_clockwise:{keywords:["sync","cycle","round","repeat"],char:"🔃",fitzpatrick_scale:false,category:"symbols"},heavy_plus_sign:{keywords:["math","calculation","addition","more","increase"],char:"➕",fitzpatrick_scale:false,category:"symbols"},heavy_minus_sign:{keywords:["math","calculation","subtract","less"],char:"➖",fitzpatrick_scale:false,category:"symbols"},heavy_division_sign:{keywords:["divide","math","calculation"],char:"➗",fitzpatrick_scale:false,category:"symbols"},heavy_multiplication_x:{keywords:["math","calculation"],char:"✖️",fitzpatrick_scale:false,category:"symbols"},infinity:{keywords:["forever"],char:"♾",fitzpatrick_scale:false,category:"symbols"},heavy_dollar_sign:{keywords:["money","sales","payment","currency","buck"],char:"💲",fitzpatrick_scale:false,category:"symbols"},currency_exchange:{keywords:["money","sales","dollar","travel"],char:"💱",fitzpatrick_scale:false,category:"symbols"},copyright:{keywords:["ip","license","circle","law","legal"],char:"©️",fitzpatrick_scale:false,category:"symbols"},registered:{keywords:["alphabet","circle"],char:"®️",fitzpatrick_scale:false,category:"symbols"},tm:{keywords:["trademark","brand","law","legal"],char:"™️",fitzpatrick_scale:false,category:"symbols"},end:{keywords:["words","arrow"],char:"🔚",fitzpatrick_scale:false,category:"symbols"},back:{keywords:["arrow","words","return"],char:"🔙",fitzpatrick_scale:false,category:"symbols"},on:{keywords:["arrow","words"],char:"🔛",fitzpatrick_scale:false,category:"symbols"},top:{keywords:["words","blue-square"],char:"🔝",fitzpatrick_scale:false,category:"symbols"},soon:{keywords:["arrow","words"],char:"🔜",fitzpatrick_scale:false,category:"symbols"},ballot_box_with_check:{keywords:["ok","agree","confirm","black-square","vote","election","yes","tick"],char:"☑️",fitzpatrick_scale:false,category:"symbols"},radio_button:{keywords:["input","old","music","circle"],char:"🔘",fitzpatrick_scale:false,category:"symbols"},white_circle:{keywords:["shape","round"],char:"⚪",fitzpatrick_scale:false,category:"symbols"},black_circle:{keywords:["shape","button","round"],char:"⚫",fitzpatrick_scale:false,category:"symbols"},red_circle:{keywords:["shape","error","danger"],char:"🔴",fitzpatrick_scale:false,category:"symbols"},large_blue_circle:{keywords:["shape","icon","button"],char:"🔵",fitzpatrick_scale:false,category:"symbols"},small_orange_diamond:{keywords:["shape","jewel","gem"],char:"🔸",fitzpatrick_scale:false,category:"symbols"},small_blue_diamond:{keywords:["shape","jewel","gem"],char:"🔹",fitzpatrick_scale:false,category:"symbols"},large_orange_diamond:{keywords:["shape","jewel","gem"],char:"🔶",fitzpatrick_scale:false,category:"symbols"},large_blue_diamond:{keywords:["shape","jewel","gem"],char:"🔷",fitzpatrick_scale:false,category:"symbols"},small_red_triangle:{keywords:["shape","direction","up","top"],char:"🔺",fitzpatrick_scale:false,category:"symbols"},black_small_square:{keywords:["shape","icon"],char:"▪️",fitzpatrick_scale:false,category:"symbols"},white_small_square:{keywords:["shape","icon"],char:"▫️",fitzpatrick_scale:false,category:"symbols"},black_large_square:{keywords:["shape","icon","button"],char:"⬛",fitzpatrick_scale:false,category:"symbols"},white_large_square:{keywords:["shape","icon","stone","button"],char:"⬜",fitzpatrick_scale:false,category:"symbols"},small_red_triangle_down:{keywords:["shape","direction","bottom"],char:"🔻",fitzpatrick_scale:false,category:"symbols"},black_medium_square:{keywords:["shape","button","icon"],char:"◼️",fitzpatrick_scale:false,category:"symbols"},white_medium_square:{keywords:["shape","stone","icon"],char:"◻️",fitzpatrick_scale:false,category:"symbols"},black_medium_small_square:{keywords:["icon","shape","button"],char:"◾",fitzpatrick_scale:false,category:"symbols"},white_medium_small_square:{keywords:["shape","stone","icon","button"],char:"◽",fitzpatrick_scale:false,category:"symbols"},black_square_button:{keywords:["shape","input","frame"],char:"🔲",fitzpatrick_scale:false,category:"symbols"},white_square_button:{keywords:["shape","input"],char:"🔳",fitzpatrick_scale:false,category:"symbols"},speaker:{keywords:["sound","volume","silence","broadcast"],char:"🔈",fitzpatrick_scale:false,category:"symbols"},sound:{keywords:["volume","speaker","broadcast"],char:"🔉",fitzpatrick_scale:false,category:"symbols"},loud_sound:{keywords:["volume","noise","noisy","speaker","broadcast"],char:"🔊",fitzpatrick_scale:false,category:"symbols"},mute:{keywords:["sound","volume","silence","quiet"],char:"🔇",fitzpatrick_scale:false,category:"symbols"},mega:{keywords:["sound","speaker","volume"],char:"📣",fitzpatrick_scale:false,category:"symbols"},loudspeaker:{keywords:["volume","sound"],char:"📢",fitzpatrick_scale:false,category:"symbols"},bell:{keywords:["sound","notification","christmas","xmas","chime"],char:"🔔",fitzpatrick_scale:false,category:"symbols"},no_bell:{keywords:["sound","volume","mute","quiet","silent"],char:"🔕",fitzpatrick_scale:false,category:"symbols"},black_joker:{keywords:["poker","cards","game","play","magic"],char:"🃏",fitzpatrick_scale:false,category:"symbols"},mahjong:{keywords:["game","play","chinese","kanji"],char:"🀄",fitzpatrick_scale:false,category:"symbols"},spades:{keywords:["poker","cards","suits","magic"],char:"♠️",fitzpatrick_scale:false,category:"symbols"},clubs:{keywords:["poker","cards","magic","suits"],char:"♣️",fitzpatrick_scale:false,category:"symbols"},hearts:{keywords:["poker","cards","magic","suits"],char:"♥️",fitzpatrick_scale:false,category:"symbols"},diamonds:{keywords:["poker","cards","magic","suits"],char:"♦️",fitzpatrick_scale:false,category:"symbols"},flower_playing_cards:{keywords:["game","sunset","red"],char:"🎴",fitzpatrick_scale:false,category:"symbols"},thought_balloon:{keywords:["bubble","cloud","speech","thinking","dream"],char:"💭",fitzpatrick_scale:false,category:"symbols"},right_anger_bubble:{keywords:["caption","speech","thinking","mad"],char:"🗯",fitzpatrick_scale:false,category:"symbols"},speech_balloon:{keywords:["bubble","words","message","talk","chatting"],char:"💬",fitzpatrick_scale:false,category:"symbols"},left_speech_bubble:{keywords:["words","message","talk","chatting"],char:"🗨",fitzpatrick_scale:false,category:"symbols"},clock1:{keywords:["time","late","early","schedule"],char:"🕐",fitzpatrick_scale:false,category:"symbols"},clock2:{keywords:["time","late","early","schedule"],char:"🕑",fitzpatrick_scale:false,category:"symbols"},clock3:{keywords:["time","late","early","schedule"],char:"🕒",fitzpatrick_scale:false,category:"symbols"},clock4:{keywords:["time","late","early","schedule"],char:"🕓",fitzpatrick_scale:false,category:"symbols"},clock5:{keywords:["time","late","early","schedule"],char:"🕔",fitzpatrick_scale:false,category:"symbols"},clock6:{keywords:["time","late","early","schedule","dawn","dusk"],char:"🕕",fitzpatrick_scale:false,category:"symbols"},clock7:{keywords:["time","late","early","schedule"],char:"🕖",fitzpatrick_scale:false,category:"symbols"},clock8:{keywords:["time","late","early","schedule"],char:"🕗",fitzpatrick_scale:false,category:"symbols"},clock9:{keywords:["time","late","early","schedule"],char:"🕘",fitzpatrick_scale:false,category:"symbols"},clock10:{keywords:["time","late","early","schedule"],char:"🕙",fitzpatrick_scale:false,category:"symbols"},clock11:{keywords:["time","late","early","schedule"],char:"🕚",fitzpatrick_scale:false,category:"symbols"},clock12:{keywords:["time","noon","midnight","midday","late","early","schedule"],char:"🕛",fitzpatrick_scale:false,category:"symbols"},clock130:{keywords:["time","late","early","schedule"],char:"🕜",fitzpatrick_scale:false,category:"symbols"},clock230:{keywords:["time","late","early","schedule"],char:"🕝",fitzpatrick_scale:false,category:"symbols"},clock330:{keywords:["time","late","early","schedule"],char:"🕞",fitzpatrick_scale:false,category:"symbols"},clock430:{keywords:["time","late","early","schedule"],char:"🕟",fitzpatrick_scale:false,category:"symbols"},clock530:{keywords:["time","late","early","schedule"],char:"🕠",fitzpatrick_scale:false,category:"symbols"},clock630:{keywords:["time","late","early","schedule"],char:"🕡",fitzpatrick_scale:false,category:"symbols"},clock730:{keywords:["time","late","early","schedule"],char:"🕢",fitzpatrick_scale:false,category:"symbols"},clock830:{keywords:["time","late","early","schedule"],char:"🕣",fitzpatrick_scale:false,category:"symbols"},clock930:{keywords:["time","late","early","schedule"],char:"🕤",fitzpatrick_scale:false,category:"symbols"},clock1030:{keywords:["time","late","early","schedule"],char:"🕥",fitzpatrick_scale:false,category:"symbols"},clock1130:{keywords:["time","late","early","schedule"],char:"🕦",fitzpatrick_scale:false,category:"symbols"},clock1230:{keywords:["time","late","early","schedule"],char:"🕧",fitzpatrick_scale:false,category:"symbols"},afghanistan:{keywords:["af","flag","nation","country","banner"],char:"🇦🇫",fitzpatrick_scale:false,category:"flags"},aland_islands:{keywords:["Åland","islands","flag","nation","country","banner"],char:"🇦🇽",fitzpatrick_scale:false,category:"flags"},albania:{keywords:["al","flag","nation","country","banner"],char:"🇦🇱",fitzpatrick_scale:false,category:"flags"},algeria:{keywords:["dz","flag","nation","country","banner"],char:"🇩🇿",fitzpatrick_scale:false,category:"flags"},american_samoa:{keywords:["american","ws","flag","nation","country","banner"],char:"🇦🇸",fitzpatrick_scale:false,category:"flags"},andorra:{keywords:["ad","flag","nation","country","banner"],char:"🇦🇩",fitzpatrick_scale:false,category:"flags"},angola:{keywords:["ao","flag","nation","country","banner"],char:"🇦🇴",fitzpatrick_scale:false,category:"flags"},anguilla:{keywords:["ai","flag","nation","country","banner"],char:"🇦🇮",fitzpatrick_scale:false,category:"flags"},antarctica:{keywords:["aq","flag","nation","country","banner"],char:"🇦🇶",fitzpatrick_scale:false,category:"flags"},antigua_barbuda:{keywords:["antigua","barbuda","flag","nation","country","banner"],char:"🇦🇬",fitzpatrick_scale:false,category:"flags"},argentina:{keywords:["ar","flag","nation","country","banner"],char:"🇦🇷",fitzpatrick_scale:false,category:"flags"},armenia:{keywords:["am","flag","nation","country","banner"],char:"🇦🇲",fitzpatrick_scale:false,category:"flags"},aruba:{keywords:["aw","flag","nation","country","banner"],char:"🇦🇼",fitzpatrick_scale:false,category:"flags"},australia:{keywords:["au","flag","nation","country","banner"],char:"🇦🇺",fitzpatrick_scale:false,category:"flags"},austria:{keywords:["at","flag","nation","country","banner"],char:"🇦🇹",fitzpatrick_scale:false,category:"flags"},azerbaijan:{keywords:["az","flag","nation","country","banner"],char:"🇦🇿",fitzpatrick_scale:false,category:"flags"},bahamas:{keywords:["bs","flag","nation","country","banner"],char:"🇧🇸",fitzpatrick_scale:false,category:"flags"},bahrain:{keywords:["bh","flag","nation","country","banner"],char:"🇧🇭",fitzpatrick_scale:false,category:"flags"},bangladesh:{keywords:["bd","flag","nation","country","banner"],char:"🇧🇩",fitzpatrick_scale:false,category:"flags"},barbados:{keywords:["bb","flag","nation","country","banner"],char:"🇧🇧",fitzpatrick_scale:false,category:"flags"},belarus:{keywords:["by","flag","nation","country","banner"],char:"🇧🇾",fitzpatrick_scale:false,category:"flags"},belgium:{keywords:["be","flag","nation","country","banner"],char:"🇧🇪",fitzpatrick_scale:false,category:"flags"},belize:{keywords:["bz","flag","nation","country","banner"],char:"🇧🇿",fitzpatrick_scale:false,category:"flags"},benin:{keywords:["bj","flag","nation","country","banner"],char:"🇧🇯",fitzpatrick_scale:false,category:"flags"},bermuda:{keywords:["bm","flag","nation","country","banner"],char:"🇧🇲",fitzpatrick_scale:false,category:"flags"},bhutan:{keywords:["bt","flag","nation","country","banner"],char:"🇧🇹",fitzpatrick_scale:false,category:"flags"},bolivia:{keywords:["bo","flag","nation","country","banner"],char:"🇧🇴",fitzpatrick_scale:false,category:"flags"},caribbean_netherlands:{keywords:["bonaire","flag","nation","country","banner"],char:"🇧🇶",fitzpatrick_scale:false,category:"flags"},bosnia_herzegovina:{keywords:["bosnia","herzegovina","flag","nation","country","banner"],char:"🇧🇦",fitzpatrick_scale:false,category:"flags"},botswana:{keywords:["bw","flag","nation","country","banner"],char:"🇧🇼",fitzpatrick_scale:false,category:"flags"},brazil:{keywords:["br","flag","nation","country","banner"],char:"🇧🇷",fitzpatrick_scale:false,category:"flags"},british_indian_ocean_territory:{keywords:["british","indian","ocean","territory","flag","nation","country","banner"],char:"🇮🇴",fitzpatrick_scale:false,category:"flags"},british_virgin_islands:{keywords:["british","virgin","islands","bvi","flag","nation","country","banner"],char:"🇻🇬",fitzpatrick_scale:false,category:"flags"},brunei:{keywords:["bn","darussalam","flag","nation","country","banner"],char:"🇧🇳",fitzpatrick_scale:false,category:"flags"},bulgaria:{keywords:["bg","flag","nation","country","banner"],char:"🇧🇬",fitzpatrick_scale:false,category:"flags"},burkina_faso:{keywords:["burkina","faso","flag","nation","country","banner"],char:"🇧🇫",fitzpatrick_scale:false,category:"flags"},burundi:{keywords:["bi","flag","nation","country","banner"],char:"🇧🇮",fitzpatrick_scale:false,category:"flags"},cape_verde:{keywords:["cabo","verde","flag","nation","country","banner"],char:"🇨🇻",fitzpatrick_scale:false,category:"flags"},cambodia:{keywords:["kh","flag","nation","country","banner"],char:"🇰🇭",fitzpatrick_scale:false,category:"flags"},cameroon:{keywords:["cm","flag","nation","country","banner"],char:"🇨🇲",fitzpatrick_scale:false,category:"flags"},canada:{keywords:["ca","flag","nation","country","banner"],char:"🇨🇦",fitzpatrick_scale:false,category:"flags"},canary_islands:{keywords:["canary","islands","flag","nation","country","banner"],char:"🇮🇨",fitzpatrick_scale:false,category:"flags"},cayman_islands:{keywords:["cayman","islands","flag","nation","country","banner"],char:"🇰🇾",fitzpatrick_scale:false,category:"flags"},central_african_republic:{keywords:["central","african","republic","flag","nation","country","banner"],char:"🇨🇫",fitzpatrick_scale:false,category:"flags"},chad:{keywords:["td","flag","nation","country","banner"],char:"🇹🇩",fitzpatrick_scale:false,category:"flags"},chile:{keywords:["flag","nation","country","banner"],char:"🇨🇱",fitzpatrick_scale:false,category:"flags"},cn:{keywords:["china","chinese","prc","flag","country","nation","banner"],char:"🇨🇳",fitzpatrick_scale:false,category:"flags"},christmas_island:{keywords:["christmas","island","flag","nation","country","banner"],char:"🇨🇽",fitzpatrick_scale:false,category:"flags"},cocos_islands:{keywords:["cocos","keeling","islands","flag","nation","country","banner"],char:"🇨🇨",fitzpatrick_scale:false,category:"flags"},colombia:{keywords:["co","flag","nation","country","banner"],char:"🇨🇴",fitzpatrick_scale:false,category:"flags"},comoros:{keywords:["km","flag","nation","country","banner"],char:"🇰🇲",fitzpatrick_scale:false,category:"flags"},congo_brazzaville:{keywords:["congo","flag","nation","country","banner"],char:"🇨🇬",fitzpatrick_scale:false,category:"flags"},congo_kinshasa:{keywords:["congo","democratic","republic","flag","nation","country","banner"],char:"🇨🇩",fitzpatrick_scale:false,category:"flags"},cook_islands:{keywords:["cook","islands","flag","nation","country","banner"],char:"🇨🇰",fitzpatrick_scale:false,category:"flags"},costa_rica:{keywords:["costa","rica","flag","nation","country","banner"],char:"🇨🇷",fitzpatrick_scale:false,category:"flags"},croatia:{keywords:["hr","flag","nation","country","banner"],char:"🇭🇷",fitzpatrick_scale:false,category:"flags"},cuba:{keywords:["cu","flag","nation","country","banner"],char:"🇨🇺",fitzpatrick_scale:false,category:"flags"},curacao:{keywords:["curaçao","flag","nation","country","banner"],char:"🇨🇼",fitzpatrick_scale:false,category:"flags"},cyprus:{keywords:["cy","flag","nation","country","banner"],char:"🇨🇾",fitzpatrick_scale:false,category:"flags"},czech_republic:{keywords:["cz","flag","nation","country","banner"],char:"🇨🇿",fitzpatrick_scale:false,category:"flags"},denmark:{keywords:["dk","flag","nation","country","banner"],char:"🇩🇰",fitzpatrick_scale:false,category:"flags"},djibouti:{keywords:["dj","flag","nation","country","banner"],char:"🇩🇯",fitzpatrick_scale:false,category:"flags"},dominica:{keywords:["dm","flag","nation","country","banner"],char:"🇩🇲",fitzpatrick_scale:false,category:"flags"},dominican_republic:{keywords:["dominican","republic","flag","nation","country","banner"],char:"🇩🇴",fitzpatrick_scale:false,category:"flags"},ecuador:{keywords:["ec","flag","nation","country","banner"],char:"🇪🇨",fitzpatrick_scale:false,category:"flags"},egypt:{keywords:["eg","flag","nation","country","banner"],char:"🇪🇬",fitzpatrick_scale:false,category:"flags"},el_salvador:{keywords:["el","salvador","flag","nation","country","banner"],char:"🇸🇻",fitzpatrick_scale:false,category:"flags"},equatorial_guinea:{keywords:["equatorial","gn","flag","nation","country","banner"],char:"🇬🇶",fitzpatrick_scale:false,category:"flags"},eritrea:{keywords:["er","flag","nation","country","banner"],char:"🇪🇷",fitzpatrick_scale:false,category:"flags"},estonia:{keywords:["ee","flag","nation","country","banner"],char:"🇪🇪",fitzpatrick_scale:false,category:"flags"},ethiopia:{keywords:["et","flag","nation","country","banner"],char:"🇪🇹",fitzpatrick_scale:false,category:"flags"},eu:{keywords:["european","union","flag","banner"],char:"🇪🇺",fitzpatrick_scale:false,category:"flags"},falkland_islands:{keywords:["falkland","islands","malvinas","flag","nation","country","banner"],char:"🇫🇰",fitzpatrick_scale:false,category:"flags"},faroe_islands:{keywords:["faroe","islands","flag","nation","country","banner"],char:"🇫🇴",fitzpatrick_scale:false,category:"flags"},fiji:{keywords:["fj","flag","nation","country","banner"],char:"🇫🇯",fitzpatrick_scale:false,category:"flags"},finland:{keywords:["fi","flag","nation","country","banner"],char:"🇫🇮",fitzpatrick_scale:false,category:"flags"},fr:{keywords:["banner","flag","nation","france","french","country"],char:"🇫🇷",fitzpatrick_scale:false,category:"flags"},french_guiana:{keywords:["french","guiana","flag","nation","country","banner"],char:"🇬🇫",fitzpatrick_scale:false,category:"flags"},french_polynesia:{keywords:["french","polynesia","flag","nation","country","banner"],char:"🇵🇫",fitzpatrick_scale:false,category:"flags"},french_southern_territories:{keywords:["french","southern","territories","flag","nation","country","banner"],char:"🇹🇫",fitzpatrick_scale:false,category:"flags"},gabon:{keywords:["ga","flag","nation","country","banner"],char:"🇬🇦",fitzpatrick_scale:false,category:"flags"},gambia:{keywords:["gm","flag","nation","country","banner"],char:"🇬🇲",fitzpatrick_scale:false,category:"flags"},georgia:{keywords:["ge","flag","nation","country","banner"],char:"🇬🇪",fitzpatrick_scale:false,category:"flags"},de:{keywords:["german","nation","flag","country","banner"],char:"🇩🇪",fitzpatrick_scale:false,category:"flags"},ghana:{keywords:["gh","flag","nation","country","banner"],char:"🇬🇭",fitzpatrick_scale:false,category:"flags"},gibraltar:{keywords:["gi","flag","nation","country","banner"],char:"🇬🇮",fitzpatrick_scale:false,category:"flags"},greece:{keywords:["gr","flag","nation","country","banner"],char:"🇬🇷",fitzpatrick_scale:false,category:"flags"},greenland:{keywords:["gl","flag","nation","country","banner"],char:"🇬🇱",fitzpatrick_scale:false,category:"flags"},grenada:{keywords:["gd","flag","nation","country","banner"],char:"🇬🇩",fitzpatrick_scale:false,category:"flags"},guadeloupe:{keywords:["gp","flag","nation","country","banner"],char:"🇬🇵",fitzpatrick_scale:false,category:"flags"},guam:{keywords:["gu","flag","nation","country","banner"],char:"🇬🇺",fitzpatrick_scale:false,category:"flags"},guatemala:{keywords:["gt","flag","nation","country","banner"],char:"🇬🇹",fitzpatrick_scale:false,category:"flags"},guernsey:{keywords:["gg","flag","nation","country","banner"],char:"🇬🇬",fitzpatrick_scale:false,category:"flags"},guinea:{keywords:["gn","flag","nation","country","banner"],char:"🇬🇳",fitzpatrick_scale:false,category:"flags"},guinea_bissau:{keywords:["gw","bissau","flag","nation","country","banner"],char:"🇬🇼",fitzpatrick_scale:false,category:"flags"},guyana:{keywords:["gy","flag","nation","country","banner"],char:"🇬🇾",fitzpatrick_scale:false,category:"flags"},haiti:{keywords:["ht","flag","nation","country","banner"],char:"🇭🇹",fitzpatrick_scale:false,category:"flags"},honduras:{keywords:["hn","flag","nation","country","banner"],char:"🇭🇳",fitzpatrick_scale:false,category:"flags"},hong_kong:{keywords:["hong","kong","flag","nation","country","banner"],char:"🇭🇰",fitzpatrick_scale:false,category:"flags"},hungary:{keywords:["hu","flag","nation","country","banner"],char:"🇭🇺",fitzpatrick_scale:false,category:"flags"},iceland:{keywords:["is","flag","nation","country","banner"],char:"🇮🇸",fitzpatrick_scale:false,category:"flags"},india:{keywords:["in","flag","nation","country","banner"],char:"🇮🇳",fitzpatrick_scale:false,category:"flags"},indonesia:{keywords:["flag","nation","country","banner"],char:"🇮🇩",fitzpatrick_scale:false,category:"flags"},iran:{keywords:["iran,","islamic","republic","flag","nation","country","banner"],char:"🇮🇷",fitzpatrick_scale:false,category:"flags"},iraq:{keywords:["iq","flag","nation","country","banner"],char:"🇮🇶",fitzpatrick_scale:false,category:"flags"},ireland:{keywords:["ie","flag","nation","country","banner"],char:"🇮🇪",fitzpatrick_scale:false,category:"flags"},isle_of_man:{keywords:["isle","man","flag","nation","country","banner"],char:"🇮🇲",fitzpatrick_scale:false,category:"flags"},israel:{keywords:["il","flag","nation","country","banner"],char:"🇮🇱",fitzpatrick_scale:false,category:"flags"},it:{keywords:["italy","flag","nation","country","banner"],char:"🇮🇹",fitzpatrick_scale:false,category:"flags"},cote_divoire:{keywords:["ivory","coast","flag","nation","country","banner"],char:"🇨🇮",fitzpatrick_scale:false,category:"flags"},jamaica:{keywords:["jm","flag","nation","country","banner"],char:"🇯🇲",fitzpatrick_scale:false,category:"flags"},jp:{keywords:["japanese","nation","flag","country","banner"],char:"🇯🇵",fitzpatrick_scale:false,category:"flags"},jersey:{keywords:["je","flag","nation","country","banner"],char:"🇯🇪",fitzpatrick_scale:false,category:"flags"},jordan:{keywords:["jo","flag","nation","country","banner"],char:"🇯🇴",fitzpatrick_scale:false,category:"flags"},kazakhstan:{keywords:["kz","flag","nation","country","banner"],char:"🇰🇿",fitzpatrick_scale:false,category:"flags"},kenya:{keywords:["ke","flag","nation","country","banner"],char:"🇰🇪",fitzpatrick_scale:false,category:"flags"},kiribati:{keywords:["ki","flag","nation","country","banner"],char:"🇰🇮",fitzpatrick_scale:false,category:"flags"},kosovo:{keywords:["xk","flag","nation","country","banner"],char:"🇽🇰",fitzpatrick_scale:false,category:"flags"},kuwait:{keywords:["kw","flag","nation","country","banner"],char:"🇰🇼",fitzpatrick_scale:false,category:"flags"},kyrgyzstan:{keywords:["kg","flag","nation","country","banner"],char:"🇰🇬",fitzpatrick_scale:false,category:"flags"},laos:{keywords:["lao","democratic","republic","flag","nation","country","banner"],char:"🇱🇦",fitzpatrick_scale:false,category:"flags"},latvia:{keywords:["lv","flag","nation","country","banner"],char:"🇱🇻",fitzpatrick_scale:false,category:"flags"},lebanon:{keywords:["lb","flag","nation","country","banner"],char:"🇱🇧",fitzpatrick_scale:false,category:"flags"},lesotho:{keywords:["ls","flag","nation","country","banner"],char:"🇱🇸",fitzpatrick_scale:false,category:"flags"},liberia:{keywords:["lr","flag","nation","country","banner"],char:"🇱🇷",fitzpatrick_scale:false,category:"flags"},libya:{keywords:["ly","flag","nation","country","banner"],char:"🇱🇾",fitzpatrick_scale:false,category:"flags"},liechtenstein:{keywords:["li","flag","nation","country","banner"],char:"🇱🇮",fitzpatrick_scale:false,category:"flags"},lithuania:{keywords:["lt","flag","nation","country","banner"],char:"🇱🇹",fitzpatrick_scale:false,category:"flags"},luxembourg:{keywords:["lu","flag","nation","country","banner"],char:"🇱🇺",fitzpatrick_scale:false,category:"flags"},macau:{keywords:["macao","flag","nation","country","banner"],char:"🇲🇴",fitzpatrick_scale:false,category:"flags"},macedonia:{keywords:["macedonia,","flag","nation","country","banner"],char:"🇲🇰",fitzpatrick_scale:false,category:"flags"},madagascar:{keywords:["mg","flag","nation","country","banner"],char:"🇲🇬",fitzpatrick_scale:false,category:"flags"},malawi:{keywords:["mw","flag","nation","country","banner"],char:"🇲🇼",fitzpatrick_scale:false,category:"flags"},malaysia:{keywords:["my","flag","nation","country","banner"],char:"🇲🇾",fitzpatrick_scale:false,category:"flags"},maldives:{keywords:["mv","flag","nation","country","banner"],char:"🇲🇻",fitzpatrick_scale:false,category:"flags"},mali:{keywords:["ml","flag","nation","country","banner"],char:"🇲🇱",fitzpatrick_scale:false,category:"flags"},malta:{keywords:["mt","flag","nation","country","banner"],char:"🇲🇹",fitzpatrick_scale:false,category:"flags"},marshall_islands:{keywords:["marshall","islands","flag","nation","country","banner"],char:"🇲🇭",fitzpatrick_scale:false,category:"flags"},martinique:{keywords:["mq","flag","nation","country","banner"],char:"🇲🇶",fitzpatrick_scale:false,category:"flags"},mauritania:{keywords:["mr","flag","nation","country","banner"],char:"🇲🇷",fitzpatrick_scale:false,category:"flags"},mauritius:{keywords:["mu","flag","nation","country","banner"],char:"🇲🇺",fitzpatrick_scale:false,category:"flags"},mayotte:{keywords:["yt","flag","nation","country","banner"],char:"🇾🇹",fitzpatrick_scale:false,category:"flags"},mexico:{keywords:["mx","flag","nation","country","banner"],char:"🇲🇽",fitzpatrick_scale:false,category:"flags"},micronesia:{keywords:["micronesia,","federated","states","flag","nation","country","banner"],char:"🇫🇲",fitzpatrick_scale:false,category:"flags"},moldova:{keywords:["moldova,","republic","flag","nation","country","banner"],char:"🇲🇩",fitzpatrick_scale:false,category:"flags"},monaco:{keywords:["mc","flag","nation","country","banner"],char:"🇲🇨",fitzpatrick_scale:false,category:"flags"},mongolia:{keywords:["mn","flag","nation","country","banner"],char:"🇲🇳",fitzpatrick_scale:false,category:"flags"},montenegro:{keywords:["me","flag","nation","country","banner"],char:"🇲🇪",fitzpatrick_scale:false,category:"flags"},montserrat:{keywords:["ms","flag","nation","country","banner"],char:"🇲🇸",fitzpatrick_scale:false,category:"flags"},morocco:{keywords:["ma","flag","nation","country","banner"],char:"🇲🇦",fitzpatrick_scale:false,category:"flags"},mozambique:{keywords:["mz","flag","nation","country","banner"],char:"🇲🇿",fitzpatrick_scale:false,category:"flags"},myanmar:{keywords:["mm","flag","nation","country","banner"],char:"🇲🇲",fitzpatrick_scale:false,category:"flags"},namibia:{keywords:["na","flag","nation","country","banner"],char:"🇳🇦",fitzpatrick_scale:false,category:"flags"},nauru:{keywords:["nr","flag","nation","country","banner"],char:"🇳🇷",fitzpatrick_scale:false,category:"flags"},nepal:{keywords:["np","flag","nation","country","banner"],char:"🇳🇵",fitzpatrick_scale:false,category:"flags"},netherlands:{keywords:["nl","flag","nation","country","banner"],char:"🇳🇱",fitzpatrick_scale:false,category:"flags"},new_caledonia:{keywords:["new","caledonia","flag","nation","country","banner"],char:"🇳🇨",fitzpatrick_scale:false,category:"flags"},new_zealand:{keywords:["new","zealand","flag","nation","country","banner"],char:"🇳🇿",fitzpatrick_scale:false,category:"flags"},nicaragua:{keywords:["ni","flag","nation","country","banner"],char:"🇳🇮",fitzpatrick_scale:false,category:"flags"},niger:{keywords:["ne","flag","nation","country","banner"],char:"🇳🇪",fitzpatrick_scale:false,category:"flags"},nigeria:{keywords:["flag","nation","country","banner"],char:"🇳🇬",fitzpatrick_scale:false,category:"flags"},niue:{keywords:["nu","flag","nation","country","banner"],char:"🇳🇺",fitzpatrick_scale:false,category:"flags"},norfolk_island:{keywords:["norfolk","island","flag","nation","country","banner"],char:"🇳🇫",fitzpatrick_scale:false,category:"flags"},northern_mariana_islands:{keywords:["northern","mariana","islands","flag","nation","country","banner"],char:"🇲🇵",fitzpatrick_scale:false,category:"flags"},north_korea:{keywords:["north","korea","nation","flag","country","banner"],char:"🇰🇵",fitzpatrick_scale:false,category:"flags"},norway:{keywords:["no","flag","nation","country","banner"],char:"🇳🇴",fitzpatrick_scale:false,category:"flags"},oman:{keywords:["om_symbol","flag","nation","country","banner"],char:"🇴🇲",fitzpatrick_scale:false,category:"flags"},pakistan:{keywords:["pk","flag","nation","country","banner"],char:"🇵🇰",fitzpatrick_scale:false,category:"flags"},palau:{keywords:["pw","flag","nation","country","banner"],char:"🇵🇼",fitzpatrick_scale:false,category:"flags"},palestinian_territories:{keywords:["palestine","palestinian","territories","flag","nation","country","banner"],char:"🇵🇸",fitzpatrick_scale:false,category:"flags"},panama:{keywords:["pa","flag","nation","country","banner"],char:"🇵🇦",fitzpatrick_scale:false,category:"flags"},papua_new_guinea:{keywords:["papua","new","guinea","flag","nation","country","banner"],char:"🇵🇬",fitzpatrick_scale:false,category:"flags"},paraguay:{keywords:["py","flag","nation","country","banner"],char:"🇵🇾",fitzpatrick_scale:false,category:"flags"},peru:{keywords:["pe","flag","nation","country","banner"],char:"🇵🇪",fitzpatrick_scale:false,category:"flags"},philippines:{keywords:["ph","flag","nation","country","banner"],char:"🇵🇭",fitzpatrick_scale:false,category:"flags"},pitcairn_islands:{keywords:["pitcairn","flag","nation","country","banner"],char:"🇵🇳",fitzpatrick_scale:false,category:"flags"},poland:{keywords:["pl","flag","nation","country","banner"],char:"🇵🇱",fitzpatrick_scale:false,category:"flags"},portugal:{keywords:["pt","flag","nation","country","banner"],char:"🇵🇹",fitzpatrick_scale:false,category:"flags"},puerto_rico:{keywords:["puerto","rico","flag","nation","country","banner"],char:"🇵🇷",fitzpatrick_scale:false,category:"flags"},qatar:{keywords:["qa","flag","nation","country","banner"],char:"🇶🇦",fitzpatrick_scale:false,category:"flags"},reunion:{keywords:["réunion","flag","nation","country","banner"],char:"🇷🇪",fitzpatrick_scale:false,category:"flags"},romania:{keywords:["ro","flag","nation","country","banner"],char:"🇷🇴",fitzpatrick_scale:false,category:"flags"},ru:{keywords:["russian","federation","flag","nation","country","banner"],char:"🇷🇺",fitzpatrick_scale:false,category:"flags"},rwanda:{keywords:["rw","flag","nation","country","banner"],char:"🇷🇼",fitzpatrick_scale:false,category:"flags"},st_barthelemy:{keywords:["saint","barthélemy","flag","nation","country","banner"],char:"🇧🇱",fitzpatrick_scale:false,category:"flags"},st_helena:{keywords:["saint","helena","ascension","tristan","cunha","flag","nation","country","banner"],char:"🇸🇭",fitzpatrick_scale:false,category:"flags"},st_kitts_nevis:{keywords:["saint","kitts","nevis","flag","nation","country","banner"],char:"🇰🇳",fitzpatrick_scale:false,category:"flags"},st_lucia:{keywords:["saint","lucia","flag","nation","country","banner"],char:"🇱🇨",fitzpatrick_scale:false,category:"flags"},st_pierre_miquelon:{keywords:["saint","pierre","miquelon","flag","nation","country","banner"],char:"🇵🇲",fitzpatrick_scale:false,category:"flags"},st_vincent_grenadines:{keywords:["saint","vincent","grenadines","flag","nation","country","banner"],char:"🇻🇨",fitzpatrick_scale:false,category:"flags"},samoa:{keywords:["ws","flag","nation","country","banner"],char:"🇼🇸",fitzpatrick_scale:false,category:"flags"},san_marino:{keywords:["san","marino","flag","nation","country","banner"],char:"🇸🇲",fitzpatrick_scale:false,category:"flags"},sao_tome_principe:{keywords:["sao","tome","principe","flag","nation","country","banner"],char:"🇸🇹",fitzpatrick_scale:false,category:"flags"},saudi_arabia:{keywords:["flag","nation","country","banner"],char:"🇸🇦",fitzpatrick_scale:false,category:"flags"},senegal:{keywords:["sn","flag","nation","country","banner"],char:"🇸🇳",fitzpatrick_scale:false,category:"flags"},serbia:{keywords:["rs","flag","nation","country","banner"],char:"🇷🇸",fitzpatrick_scale:false,category:"flags"},seychelles:{keywords:["sc","flag","nation","country","banner"],char:"🇸🇨",fitzpatrick_scale:false,category:"flags"},sierra_leone:{keywords:["sierra","leone","flag","nation","country","banner"],char:"🇸🇱",fitzpatrick_scale:false,category:"flags"},singapore:{keywords:["sg","flag","nation","country","banner"],char:"🇸🇬",fitzpatrick_scale:false,category:"flags"},sint_maarten:{keywords:["sint","maarten","dutch","flag","nation","country","banner"],char:"🇸🇽",fitzpatrick_scale:false,category:"flags"},slovakia:{keywords:["sk","flag","nation","country","banner"],char:"🇸🇰",fitzpatrick_scale:false,category:"flags"},slovenia:{keywords:["si","flag","nation","country","banner"],char:"🇸🇮",fitzpatrick_scale:false,category:"flags"},solomon_islands:{keywords:["solomon","islands","flag","nation","country","banner"],char:"🇸🇧",fitzpatrick_scale:false,category:"flags"},somalia:{keywords:["so","flag","nation","country","banner"],char:"🇸🇴",fitzpatrick_scale:false,category:"flags"},south_africa:{keywords:["south","africa","flag","nation","country","banner"],char:"🇿🇦",fitzpatrick_scale:false,category:"flags"},south_georgia_south_sandwich_islands:{keywords:["south","georgia","sandwich","islands","flag","nation","country","banner"],char:"🇬🇸",fitzpatrick_scale:false,category:"flags"},kr:{keywords:["south","korea","nation","flag","country","banner"],char:"🇰🇷",fitzpatrick_scale:false,category:"flags"},south_sudan:{keywords:["south","sd","flag","nation","country","banner"],char:"🇸🇸",fitzpatrick_scale:false,category:"flags"},es:{keywords:["spain","flag","nation","country","banner"],char:"🇪🇸",fitzpatrick_scale:false,category:"flags"},sri_lanka:{keywords:["sri","lanka","flag","nation","country","banner"],char:"🇱🇰",fitzpatrick_scale:false,category:"flags"},sudan:{keywords:["sd","flag","nation","country","banner"],char:"🇸🇩",fitzpatrick_scale:false,category:"flags"},suriname:{keywords:["sr","flag","nation","country","banner"],char:"🇸🇷",fitzpatrick_scale:false,category:"flags"},swaziland:{keywords:["sz","flag","nation","country","banner"],char:"🇸🇿",fitzpatrick_scale:false,category:"flags"},sweden:{keywords:["se","flag","nation","country","banner"],char:"🇸🇪",fitzpatrick_scale:false,category:"flags"},switzerland:{keywords:["ch","flag","nation","country","banner"],char:"🇨🇭",fitzpatrick_scale:false,category:"flags"},syria:{keywords:["syrian","arab","republic","flag","nation","country","banner"],char:"🇸🇾",fitzpatrick_scale:false,category:"flags"},taiwan:{keywords:["tw","flag","nation","country","banner"],char:"🇹🇼",fitzpatrick_scale:false,category:"flags"},tajikistan:{keywords:["tj","flag","nation","country","banner"],char:"🇹🇯",fitzpatrick_scale:false,category:"flags"},tanzania:{keywords:["tanzania,","united","republic","flag","nation","country","banner"],char:"🇹🇿",fitzpatrick_scale:false,category:"flags"},thailand:{keywords:["th","flag","nation","country","banner"],char:"🇹🇭",fitzpatrick_scale:false,category:"flags"},timor_leste:{keywords:["timor","leste","flag","nation","country","banner"],char:"🇹🇱",fitzpatrick_scale:false,category:"flags"},togo:{keywords:["tg","flag","nation","country","banner"],char:"🇹🇬",fitzpatrick_scale:false,category:"flags"},tokelau:{keywords:["tk","flag","nation","country","banner"],char:"🇹🇰",fitzpatrick_scale:false,category:"flags"},tonga:{keywords:["to","flag","nation","country","banner"],char:"🇹🇴",fitzpatrick_scale:false,category:"flags"},trinidad_tobago:{keywords:["trinidad","tobago","flag","nation","country","banner"],char:"🇹🇹",fitzpatrick_scale:false,category:"flags"},tunisia:{keywords:["tn","flag","nation","country","banner"],char:"🇹🇳",fitzpatrick_scale:false,category:"flags"},tr:{keywords:["turkey","flag","nation","country","banner"],char:"🇹🇷",fitzpatrick_scale:false,category:"flags"},turkmenistan:{keywords:["flag","nation","country","banner"],char:"🇹🇲",fitzpatrick_scale:false,category:"flags"},turks_caicos_islands:{keywords:["turks","caicos","islands","flag","nation","country","banner"],char:"🇹🇨",fitzpatrick_scale:false,category:"flags"},tuvalu:{keywords:["flag","nation","country","banner"],char:"🇹🇻",fitzpatrick_scale:false,category:"flags"},uganda:{keywords:["ug","flag","nation","country","banner"],char:"🇺🇬",fitzpatrick_scale:false,category:"flags"},ukraine:{keywords:["ua","flag","nation","country","banner"],char:"🇺🇦",fitzpatrick_scale:false,category:"flags"},united_arab_emirates:{keywords:["united","arab","emirates","flag","nation","country","banner"],char:"🇦🇪",fitzpatrick_scale:false,category:"flags"},uk:{keywords:["united","kingdom","great","britain","northern","ireland","flag","nation","country","banner","british","UK","english","england","union jack"],char:"🇬🇧",fitzpatrick_scale:false,category:"flags"},england:{keywords:["flag","english"],char:"🏴󠁧󠁢󠁥󠁮󠁧󠁿",fitzpatrick_scale:false,category:"flags"},scotland:{keywords:["flag","scottish"],char:"🏴󠁧󠁢󠁳󠁣󠁴󠁿",fitzpatrick_scale:false,category:"flags"},wales:{keywords:["flag","welsh"],char:"🏴󠁧󠁢󠁷󠁬󠁳󠁿",fitzpatrick_scale:false,category:"flags"},us:{keywords:["united","states","america","flag","nation","country","banner"],char:"🇺🇸",fitzpatrick_scale:false,category:"flags"},us_virgin_islands:{keywords:["virgin","islands","us","flag","nation","country","banner"],char:"🇻🇮",fitzpatrick_scale:false,category:"flags"},uruguay:{keywords:["uy","flag","nation","country","banner"],char:"🇺🇾",fitzpatrick_scale:false,category:"flags"},uzbekistan:{keywords:["uz","flag","nation","country","banner"],char:"🇺🇿",fitzpatrick_scale:false,category:"flags"},vanuatu:{keywords:["vu","flag","nation","country","banner"],char:"🇻🇺",fitzpatrick_scale:false,category:"flags"},vatican_city:{keywords:["vatican","city","flag","nation","country","banner"],char:"🇻🇦",fitzpatrick_scale:false,category:"flags"},venezuela:{keywords:["ve","bolivarian","republic","flag","nation","country","banner"],char:"🇻🇪",fitzpatrick_scale:false,category:"flags"},vietnam:{keywords:["viet","nam","flag","nation","country","banner"],char:"🇻🇳",fitzpatrick_scale:false,category:"flags"},wallis_futuna:{keywords:["wallis","futuna","flag","nation","country","banner"],char:"🇼🇫",fitzpatrick_scale:false,category:"flags"},western_sahara:{keywords:["western","sahara","flag","nation","country","banner"],char:"🇪🇭",fitzpatrick_scale:false,category:"flags"},yemen:{keywords:["ye","flag","nation","country","banner"],char:"🇾🇪",fitzpatrick_scale:false,category:"flags"},zambia:{keywords:["zm","flag","nation","country","banner"],char:"🇿🇲",fitzpatrick_scale:false,category:"flags"},zimbabwe:{keywords:["zw","flag","nation","country","banner"],char:"🇿🇼",fitzpatrick_scale:false,category:"flags"},united_nations:{keywords:["un","flag","banner"],char:"🇺🇳",fitzpatrick_scale:false,category:"flags"},pirate_flag:{keywords:["skull","crossbones","flag","banner"],char:"🏴‍☠️",fitzpatrick_scale:false,category:"flags"}}); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/emoticons/js/emojis.min.js b/apps/web-antd/public/tinymce/plugins/emoticons/js/emojis.min.js new file mode 100644 index 0000000..5a1c491 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/emoticons/js/emojis.min.js @@ -0,0 +1,2 @@ +// Source: npm package: emojilib, file:emojis.json +window.tinymce.Resource.add("tinymce.plugins.emoticons",{grinning:{keywords:["face","smile","happy","joy",":D","grin"],char:"\u{1f600}",fitzpatrick_scale:!1,category:"people"},grimacing:{keywords:["face","grimace","teeth"],char:"\u{1f62c}",fitzpatrick_scale:!1,category:"people"},grin:{keywords:["face","happy","smile","joy","kawaii"],char:"\u{1f601}",fitzpatrick_scale:!1,category:"people"},joy:{keywords:["face","cry","tears","weep","happy","happytears","haha"],char:"\u{1f602}",fitzpatrick_scale:!1,category:"people"},rofl:{keywords:["face","rolling","floor","laughing","lol","haha"],char:"\u{1f923}",fitzpatrick_scale:!1,category:"people"},partying:{keywords:["face","celebration","woohoo"],char:"\u{1f973}",fitzpatrick_scale:!1,category:"people"},smiley:{keywords:["face","happy","joy","haha",":D",":)","smile","funny"],char:"\u{1f603}",fitzpatrick_scale:!1,category:"people"},smile:{keywords:["face","happy","joy","funny","haha","laugh","like",":D",":)"],char:"\u{1f604}",fitzpatrick_scale:!1,category:"people"},sweat_smile:{keywords:["face","hot","happy","laugh","sweat","smile","relief"],char:"\u{1f605}",fitzpatrick_scale:!1,category:"people"},laughing:{keywords:["happy","joy","lol","satisfied","haha","face","glad","XD","laugh"],char:"\u{1f606}",fitzpatrick_scale:!1,category:"people"},innocent:{keywords:["face","angel","heaven","halo"],char:"\u{1f607}",fitzpatrick_scale:!1,category:"people"},wink:{keywords:["face","happy","mischievous","secret",";)","smile","eye"],char:"\u{1f609}",fitzpatrick_scale:!1,category:"people"},blush:{keywords:["face","smile","happy","flushed","crush","embarrassed","shy","joy"],char:"\u{1f60a}",fitzpatrick_scale:!1,category:"people"},slightly_smiling_face:{keywords:["face","smile"],char:"\u{1f642}",fitzpatrick_scale:!1,category:"people"},upside_down_face:{keywords:["face","flipped","silly","smile"],char:"\u{1f643}",fitzpatrick_scale:!1,category:"people"},relaxed:{keywords:["face","blush","massage","happiness"],char:"\u263a\ufe0f",fitzpatrick_scale:!1,category:"people"},yum:{keywords:["happy","joy","tongue","smile","face","silly","yummy","nom","delicious","savouring"],char:"\u{1f60b}",fitzpatrick_scale:!1,category:"people"},relieved:{keywords:["face","relaxed","phew","massage","happiness"],char:"\u{1f60c}",fitzpatrick_scale:!1,category:"people"},heart_eyes:{keywords:["face","love","like","affection","valentines","infatuation","crush","heart"],char:"\u{1f60d}",fitzpatrick_scale:!1,category:"people"},smiling_face_with_three_hearts:{keywords:["face","love","like","affection","valentines","infatuation","crush","hearts","adore"],char:"\u{1f970}",fitzpatrick_scale:!1,category:"people"},kissing_heart:{keywords:["face","love","like","affection","valentines","infatuation","kiss"],char:"\u{1f618}",fitzpatrick_scale:!1,category:"people"},kissing:{keywords:["love","like","face","3","valentines","infatuation","kiss"],char:"\u{1f617}",fitzpatrick_scale:!1,category:"people"},kissing_smiling_eyes:{keywords:["face","affection","valentines","infatuation","kiss"],char:"\u{1f619}",fitzpatrick_scale:!1,category:"people"},kissing_closed_eyes:{keywords:["face","love","like","affection","valentines","infatuation","kiss"],char:"\u{1f61a}",fitzpatrick_scale:!1,category:"people"},stuck_out_tongue_winking_eye:{keywords:["face","prank","childish","playful","mischievous","smile","wink","tongue"],char:"\u{1f61c}",fitzpatrick_scale:!1,category:"people"},zany:{keywords:["face","goofy","crazy"],char:"\u{1f92a}",fitzpatrick_scale:!1,category:"people"},raised_eyebrow:{keywords:["face","distrust","scepticism","disapproval","disbelief","surprise"],char:"\u{1f928}",fitzpatrick_scale:!1,category:"people"},monocle:{keywords:["face","stuffy","wealthy"],char:"\u{1f9d0}",fitzpatrick_scale:!1,category:"people"},stuck_out_tongue_closed_eyes:{keywords:["face","prank","playful","mischievous","smile","tongue"],char:"\u{1f61d}",fitzpatrick_scale:!1,category:"people"},stuck_out_tongue:{keywords:["face","prank","childish","playful","mischievous","smile","tongue"],char:"\u{1f61b}",fitzpatrick_scale:!1,category:"people"},money_mouth_face:{keywords:["face","rich","dollar","money"],char:"\u{1f911}",fitzpatrick_scale:!1,category:"people"},nerd_face:{keywords:["face","nerdy","geek","dork"],char:"\u{1f913}",fitzpatrick_scale:!1,category:"people"},sunglasses:{keywords:["face","cool","smile","summer","beach","sunglass"],char:"\u{1f60e}",fitzpatrick_scale:!1,category:"people"},star_struck:{keywords:["face","smile","starry","eyes","grinning"],char:"\u{1f929}",fitzpatrick_scale:!1,category:"people"},clown_face:{keywords:["face"],char:"\u{1f921}",fitzpatrick_scale:!1,category:"people"},cowboy_hat_face:{keywords:["face","cowgirl","hat"],char:"\u{1f920}",fitzpatrick_scale:!1,category:"people"},hugs:{keywords:["face","smile","hug"],char:"\u{1f917}",fitzpatrick_scale:!1,category:"people"},smirk:{keywords:["face","smile","mean","prank","smug","sarcasm"],char:"\u{1f60f}",fitzpatrick_scale:!1,category:"people"},no_mouth:{keywords:["face","hellokitty"],char:"\u{1f636}",fitzpatrick_scale:!1,category:"people"},neutral_face:{keywords:["indifference","meh",":|","neutral"],char:"\u{1f610}",fitzpatrick_scale:!1,category:"people"},expressionless:{keywords:["face","indifferent","-_-","meh","deadpan"],char:"\u{1f611}",fitzpatrick_scale:!1,category:"people"},unamused:{keywords:["indifference","bored","straight face","serious","sarcasm","unimpressed","skeptical","dubious","side_eye"],char:"\u{1f612}",fitzpatrick_scale:!1,category:"people"},roll_eyes:{keywords:["face","eyeroll","frustrated"],char:"\u{1f644}",fitzpatrick_scale:!1,category:"people"},thinking:{keywords:["face","hmmm","think","consider"],char:"\u{1f914}",fitzpatrick_scale:!1,category:"people"},lying_face:{keywords:["face","lie","pinocchio"],char:"\u{1f925}",fitzpatrick_scale:!1,category:"people"},hand_over_mouth:{keywords:["face","whoops","shock","surprise"],char:"\u{1f92d}",fitzpatrick_scale:!1,category:"people"},shushing:{keywords:["face","quiet","shhh"],char:"\u{1f92b}",fitzpatrick_scale:!1,category:"people"},symbols_over_mouth:{keywords:["face","swearing","cursing","cussing","profanity","expletive"],char:"\u{1f92c}",fitzpatrick_scale:!1,category:"people"},exploding_head:{keywords:["face","shocked","mind","blown"],char:"\u{1f92f}",fitzpatrick_scale:!1,category:"people"},flushed:{keywords:["face","blush","shy","flattered"],char:"\u{1f633}",fitzpatrick_scale:!1,category:"people"},disappointed:{keywords:["face","sad","upset","depressed",":("],char:"\u{1f61e}",fitzpatrick_scale:!1,category:"people"},worried:{keywords:["face","concern","nervous",":("],char:"\u{1f61f}",fitzpatrick_scale:!1,category:"people"},angry:{keywords:["mad","face","annoyed","frustrated"],char:"\u{1f620}",fitzpatrick_scale:!1,category:"people"},rage:{keywords:["angry","mad","hate","despise"],char:"\u{1f621}",fitzpatrick_scale:!1,category:"people"},pensive:{keywords:["face","sad","depressed","upset"],char:"\u{1f614}",fitzpatrick_scale:!1,category:"people"},confused:{keywords:["face","indifference","huh","weird","hmmm",":/"],char:"\u{1f615}",fitzpatrick_scale:!1,category:"people"},slightly_frowning_face:{keywords:["face","frowning","disappointed","sad","upset"],char:"\u{1f641}",fitzpatrick_scale:!1,category:"people"},frowning_face:{keywords:["face","sad","upset","frown"],char:"\u2639",fitzpatrick_scale:!1,category:"people"},persevere:{keywords:["face","sick","no","upset","oops"],char:"\u{1f623}",fitzpatrick_scale:!1,category:"people"},confounded:{keywords:["face","confused","sick","unwell","oops",":S"],char:"\u{1f616}",fitzpatrick_scale:!1,category:"people"},tired_face:{keywords:["sick","whine","upset","frustrated"],char:"\u{1f62b}",fitzpatrick_scale:!1,category:"people"},weary:{keywords:["face","tired","sleepy","sad","frustrated","upset"],char:"\u{1f629}",fitzpatrick_scale:!1,category:"people"},pleading:{keywords:["face","begging","mercy"],char:"\u{1f97a}",fitzpatrick_scale:!1,category:"people"},triumph:{keywords:["face","gas","phew","proud","pride"],char:"\u{1f624}",fitzpatrick_scale:!1,category:"people"},open_mouth:{keywords:["face","surprise","impressed","wow","whoa",":O"],char:"\u{1f62e}",fitzpatrick_scale:!1,category:"people"},scream:{keywords:["face","munch","scared","omg"],char:"\u{1f631}",fitzpatrick_scale:!1,category:"people"},fearful:{keywords:["face","scared","terrified","nervous","oops","huh"],char:"\u{1f628}",fitzpatrick_scale:!1,category:"people"},cold_sweat:{keywords:["face","nervous","sweat"],char:"\u{1f630}",fitzpatrick_scale:!1,category:"people"},hushed:{keywords:["face","woo","shh"],char:"\u{1f62f}",fitzpatrick_scale:!1,category:"people"},frowning:{keywords:["face","aw","what"],char:"\u{1f626}",fitzpatrick_scale:!1,category:"people"},anguished:{keywords:["face","stunned","nervous"],char:"\u{1f627}",fitzpatrick_scale:!1,category:"people"},cry:{keywords:["face","tears","sad","depressed","upset",":'("],char:"\u{1f622}",fitzpatrick_scale:!1,category:"people"},disappointed_relieved:{keywords:["face","phew","sweat","nervous"],char:"\u{1f625}",fitzpatrick_scale:!1,category:"people"},drooling_face:{keywords:["face"],char:"\u{1f924}",fitzpatrick_scale:!1,category:"people"},sleepy:{keywords:["face","tired","rest","nap"],char:"\u{1f62a}",fitzpatrick_scale:!1,category:"people"},sweat:{keywords:["face","hot","sad","tired","exercise"],char:"\u{1f613}",fitzpatrick_scale:!1,category:"people"},hot:{keywords:["face","feverish","heat","red","sweating"],char:"\u{1f975}",fitzpatrick_scale:!1,category:"people"},cold:{keywords:["face","blue","freezing","frozen","frostbite","icicles"],char:"\u{1f976}",fitzpatrick_scale:!1,category:"people"},sob:{keywords:["face","cry","tears","sad","upset","depressed"],char:"\u{1f62d}",fitzpatrick_scale:!1,category:"people"},dizzy_face:{keywords:["spent","unconscious","xox","dizzy"],char:"\u{1f635}",fitzpatrick_scale:!1,category:"people"},astonished:{keywords:["face","xox","surprised","poisoned"],char:"\u{1f632}",fitzpatrick_scale:!1,category:"people"},zipper_mouth_face:{keywords:["face","sealed","zipper","secret"],char:"\u{1f910}",fitzpatrick_scale:!1,category:"people"},nauseated_face:{keywords:["face","vomit","gross","green","sick","throw up","ill"],char:"\u{1f922}",fitzpatrick_scale:!1,category:"people"},sneezing_face:{keywords:["face","gesundheit","sneeze","sick","allergy"],char:"\u{1f927}",fitzpatrick_scale:!1,category:"people"},vomiting:{keywords:["face","sick"],char:"\u{1f92e}",fitzpatrick_scale:!1,category:"people"},mask:{keywords:["face","sick","ill","disease"],char:"\u{1f637}",fitzpatrick_scale:!1,category:"people"},face_with_thermometer:{keywords:["sick","temperature","thermometer","cold","fever"],char:"\u{1f912}",fitzpatrick_scale:!1,category:"people"},face_with_head_bandage:{keywords:["injured","clumsy","bandage","hurt"],char:"\u{1f915}",fitzpatrick_scale:!1,category:"people"},woozy:{keywords:["face","dizzy","intoxicated","tipsy","wavy"],char:"\u{1f974}",fitzpatrick_scale:!1,category:"people"},sleeping:{keywords:["face","tired","sleepy","night","zzz"],char:"\u{1f634}",fitzpatrick_scale:!1,category:"people"},zzz:{keywords:["sleepy","tired","dream"],char:"\u{1f4a4}",fitzpatrick_scale:!1,category:"people"},poop:{keywords:["hankey","shitface","fail","turd","shit"],char:"\u{1f4a9}",fitzpatrick_scale:!1,category:"people"},smiling_imp:{keywords:["devil","horns"],char:"\u{1f608}",fitzpatrick_scale:!1,category:"people"},imp:{keywords:["devil","angry","horns"],char:"\u{1f47f}",fitzpatrick_scale:!1,category:"people"},japanese_ogre:{keywords:["monster","red","mask","halloween","scary","creepy","devil","demon","japanese","ogre"],char:"\u{1f479}",fitzpatrick_scale:!1,category:"people"},japanese_goblin:{keywords:["red","evil","mask","monster","scary","creepy","japanese","goblin"],char:"\u{1f47a}",fitzpatrick_scale:!1,category:"people"},skull:{keywords:["dead","skeleton","creepy","death"],char:"\u{1f480}",fitzpatrick_scale:!1,category:"people"},ghost:{keywords:["halloween","spooky","scary"],char:"\u{1f47b}",fitzpatrick_scale:!1,category:"people"},alien:{keywords:["UFO","paul","weird","outer_space"],char:"\u{1f47d}",fitzpatrick_scale:!1,category:"people"},robot:{keywords:["computer","machine","bot"],char:"\u{1f916}",fitzpatrick_scale:!1,category:"people"},smiley_cat:{keywords:["animal","cats","happy","smile"],char:"\u{1f63a}",fitzpatrick_scale:!1,category:"people"},smile_cat:{keywords:["animal","cats","smile"],char:"\u{1f638}",fitzpatrick_scale:!1,category:"people"},joy_cat:{keywords:["animal","cats","haha","happy","tears"],char:"\u{1f639}",fitzpatrick_scale:!1,category:"people"},heart_eyes_cat:{keywords:["animal","love","like","affection","cats","valentines","heart"],char:"\u{1f63b}",fitzpatrick_scale:!1,category:"people"},smirk_cat:{keywords:["animal","cats","smirk"],char:"\u{1f63c}",fitzpatrick_scale:!1,category:"people"},kissing_cat:{keywords:["animal","cats","kiss"],char:"\u{1f63d}",fitzpatrick_scale:!1,category:"people"},scream_cat:{keywords:["animal","cats","munch","scared","scream"],char:"\u{1f640}",fitzpatrick_scale:!1,category:"people"},crying_cat_face:{keywords:["animal","tears","weep","sad","cats","upset","cry"],char:"\u{1f63f}",fitzpatrick_scale:!1,category:"people"},pouting_cat:{keywords:["animal","cats"],char:"\u{1f63e}",fitzpatrick_scale:!1,category:"people"},palms_up:{keywords:["hands","gesture","cupped","prayer"],char:"\u{1f932}",fitzpatrick_scale:!0,category:"people"},raised_hands:{keywords:["gesture","hooray","yea","celebration","hands"],char:"\u{1f64c}",fitzpatrick_scale:!0,category:"people"},clap:{keywords:["hands","praise","applause","congrats","yay"],char:"\u{1f44f}",fitzpatrick_scale:!0,category:"people"},wave:{keywords:["hands","gesture","goodbye","solong","farewell","hello","hi","palm"],char:"\u{1f44b}",fitzpatrick_scale:!0,category:"people"},call_me_hand:{keywords:["hands","gesture"],char:"\u{1f919}",fitzpatrick_scale:!0,category:"people"},"+1":{keywords:["thumbsup","yes","awesome","good","agree","accept","cool","hand","like"],char:"\u{1f44d}",fitzpatrick_scale:!0,category:"people"},"-1":{keywords:["thumbsdown","no","dislike","hand"],char:"\u{1f44e}",fitzpatrick_scale:!0,category:"people"},facepunch:{keywords:["angry","violence","fist","hit","attack","hand"],char:"\u{1f44a}",fitzpatrick_scale:!0,category:"people"},fist:{keywords:["fingers","hand","grasp"],char:"\u270a",fitzpatrick_scale:!0,category:"people"},fist_left:{keywords:["hand","fistbump"],char:"\u{1f91b}",fitzpatrick_scale:!0,category:"people"},fist_right:{keywords:["hand","fistbump"],char:"\u{1f91c}",fitzpatrick_scale:!0,category:"people"},v:{keywords:["fingers","ohyeah","hand","peace","victory","two"],char:"\u270c",fitzpatrick_scale:!0,category:"people"},ok_hand:{keywords:["fingers","limbs","perfect","ok","okay"],char:"\u{1f44c}",fitzpatrick_scale:!0,category:"people"},raised_hand:{keywords:["fingers","stop","highfive","palm","ban"],char:"\u270b",fitzpatrick_scale:!0,category:"people"},raised_back_of_hand:{keywords:["fingers","raised","backhand"],char:"\u{1f91a}",fitzpatrick_scale:!0,category:"people"},open_hands:{keywords:["fingers","butterfly","hands","open"],char:"\u{1f450}",fitzpatrick_scale:!0,category:"people"},muscle:{keywords:["arm","flex","hand","summer","strong","biceps"],char:"\u{1f4aa}",fitzpatrick_scale:!0,category:"people"},pray:{keywords:["please","hope","wish","namaste","highfive"],char:"\u{1f64f}",fitzpatrick_scale:!0,category:"people"},foot:{keywords:["kick","stomp"],char:"\u{1f9b6}",fitzpatrick_scale:!0,category:"people"},leg:{keywords:["kick","limb"],char:"\u{1f9b5}",fitzpatrick_scale:!0,category:"people"},handshake:{keywords:["agreement","shake"],char:"\u{1f91d}",fitzpatrick_scale:!1,category:"people"},point_up:{keywords:["hand","fingers","direction","up"],char:"\u261d",fitzpatrick_scale:!0,category:"people"},point_up_2:{keywords:["fingers","hand","direction","up"],char:"\u{1f446}",fitzpatrick_scale:!0,category:"people"},point_down:{keywords:["fingers","hand","direction","down"],char:"\u{1f447}",fitzpatrick_scale:!0,category:"people"},point_left:{keywords:["direction","fingers","hand","left"],char:"\u{1f448}",fitzpatrick_scale:!0,category:"people"},point_right:{keywords:["fingers","hand","direction","right"],char:"\u{1f449}",fitzpatrick_scale:!0,category:"people"},fu:{keywords:["hand","fingers","rude","middle","flipping"],char:"\u{1f595}",fitzpatrick_scale:!0,category:"people"},raised_hand_with_fingers_splayed:{keywords:["hand","fingers","palm"],char:"\u{1f590}",fitzpatrick_scale:!0,category:"people"},love_you:{keywords:["hand","fingers","gesture"],char:"\u{1f91f}",fitzpatrick_scale:!0,category:"people"},metal:{keywords:["hand","fingers","evil_eye","sign_of_horns","rock_on"],char:"\u{1f918}",fitzpatrick_scale:!0,category:"people"},crossed_fingers:{keywords:["good","lucky"],char:"\u{1f91e}",fitzpatrick_scale:!0,category:"people"},vulcan_salute:{keywords:["hand","fingers","spock","star trek"],char:"\u{1f596}",fitzpatrick_scale:!0,category:"people"},writing_hand:{keywords:["lower_left_ballpoint_pen","stationery","write","compose"],char:"\u270d",fitzpatrick_scale:!0,category:"people"},selfie:{keywords:["camera","phone"],char:"\u{1f933}",fitzpatrick_scale:!0,category:"people"},nail_care:{keywords:["beauty","manicure","finger","fashion","nail"],char:"\u{1f485}",fitzpatrick_scale:!0,category:"people"},lips:{keywords:["mouth","kiss"],char:"\u{1f444}",fitzpatrick_scale:!1,category:"people"},tooth:{keywords:["teeth","dentist"],char:"\u{1f9b7}",fitzpatrick_scale:!1,category:"people"},tongue:{keywords:["mouth","playful"],char:"\u{1f445}",fitzpatrick_scale:!1,category:"people"},ear:{keywords:["face","hear","sound","listen"],char:"\u{1f442}",fitzpatrick_scale:!0,category:"people"},nose:{keywords:["smell","sniff"],char:"\u{1f443}",fitzpatrick_scale:!0,category:"people"},eye:{keywords:["face","look","see","watch","stare"],char:"\u{1f441}",fitzpatrick_scale:!1,category:"people"},eyes:{keywords:["look","watch","stalk","peek","see"],char:"\u{1f440}",fitzpatrick_scale:!1,category:"people"},brain:{keywords:["smart","intelligent"],char:"\u{1f9e0}",fitzpatrick_scale:!1,category:"people"},bust_in_silhouette:{keywords:["user","person","human"],char:"\u{1f464}",fitzpatrick_scale:!1,category:"people"},busts_in_silhouette:{keywords:["user","person","human","group","team"],char:"\u{1f465}",fitzpatrick_scale:!1,category:"people"},speaking_head:{keywords:["user","person","human","sing","say","talk"],char:"\u{1f5e3}",fitzpatrick_scale:!1,category:"people"},baby:{keywords:["child","boy","girl","toddler"],char:"\u{1f476}",fitzpatrick_scale:!0,category:"people"},child:{keywords:["gender-neutral","young"],char:"\u{1f9d2}",fitzpatrick_scale:!0,category:"people"},boy:{keywords:["man","male","guy","teenager"],char:"\u{1f466}",fitzpatrick_scale:!0,category:"people"},girl:{keywords:["female","woman","teenager"],char:"\u{1f467}",fitzpatrick_scale:!0,category:"people"},adult:{keywords:["gender-neutral","person"],char:"\u{1f9d1}",fitzpatrick_scale:!0,category:"people"},man:{keywords:["mustache","father","dad","guy","classy","sir","moustache"],char:"\u{1f468}",fitzpatrick_scale:!0,category:"people"},woman:{keywords:["female","girls","lady"],char:"\u{1f469}",fitzpatrick_scale:!0,category:"people"},blonde_woman:{keywords:["woman","female","girl","blonde","person"],char:"\u{1f471}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"people"},blonde_man:{keywords:["man","male","boy","blonde","guy","person"],char:"\u{1f471}",fitzpatrick_scale:!0,category:"people"},bearded_person:{keywords:["person","bewhiskered"],char:"\u{1f9d4}",fitzpatrick_scale:!0,category:"people"},older_adult:{keywords:["human","elder","senior","gender-neutral"],char:"\u{1f9d3}",fitzpatrick_scale:!0,category:"people"},older_man:{keywords:["human","male","men","old","elder","senior"],char:"\u{1f474}",fitzpatrick_scale:!0,category:"people"},older_woman:{keywords:["human","female","women","lady","old","elder","senior"],char:"\u{1f475}",fitzpatrick_scale:!0,category:"people"},man_with_gua_pi_mao:{keywords:["male","boy","chinese"],char:"\u{1f472}",fitzpatrick_scale:!0,category:"people"},woman_with_headscarf:{keywords:["female","hijab","mantilla","tichel"],char:"\u{1f9d5}",fitzpatrick_scale:!0,category:"people"},woman_with_turban:{keywords:["female","indian","hinduism","arabs","woman"],char:"\u{1f473}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"people"},man_with_turban:{keywords:["male","indian","hinduism","arabs"],char:"\u{1f473}",fitzpatrick_scale:!0,category:"people"},policewoman:{keywords:["woman","police","law","legal","enforcement","arrest","911","female"],char:"\u{1f46e}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"people"},policeman:{keywords:["man","police","law","legal","enforcement","arrest","911"],char:"\u{1f46e}",fitzpatrick_scale:!0,category:"people"},construction_worker_woman:{keywords:["female","human","wip","build","construction","worker","labor","woman"],char:"\u{1f477}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"people"},construction_worker_man:{keywords:["male","human","wip","guy","build","construction","worker","labor"],char:"\u{1f477}",fitzpatrick_scale:!0,category:"people"},guardswoman:{keywords:["uk","gb","british","female","royal","woman"],char:"\u{1f482}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"people"},guardsman:{keywords:["uk","gb","british","male","guy","royal"],char:"\u{1f482}",fitzpatrick_scale:!0,category:"people"},female_detective:{keywords:["human","spy","detective","female","woman"],char:"\u{1f575}\ufe0f\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"people"},male_detective:{keywords:["human","spy","detective"],char:"\u{1f575}",fitzpatrick_scale:!0,category:"people"},woman_health_worker:{keywords:["doctor","nurse","therapist","healthcare","woman","human"],char:"\u{1f469}\u200d\u2695\ufe0f",fitzpatrick_scale:!0,category:"people"},man_health_worker:{keywords:["doctor","nurse","therapist","healthcare","man","human"],char:"\u{1f468}\u200d\u2695\ufe0f",fitzpatrick_scale:!0,category:"people"},woman_farmer:{keywords:["rancher","gardener","woman","human"],char:"\u{1f469}\u200d\u{1f33e}",fitzpatrick_scale:!0,category:"people"},man_farmer:{keywords:["rancher","gardener","man","human"],char:"\u{1f468}\u200d\u{1f33e}",fitzpatrick_scale:!0,category:"people"},woman_cook:{keywords:["chef","woman","human"],char:"\u{1f469}\u200d\u{1f373}",fitzpatrick_scale:!0,category:"people"},man_cook:{keywords:["chef","man","human"],char:"\u{1f468}\u200d\u{1f373}",fitzpatrick_scale:!0,category:"people"},woman_student:{keywords:["graduate","woman","human"],char:"\u{1f469}\u200d\u{1f393}",fitzpatrick_scale:!0,category:"people"},man_student:{keywords:["graduate","man","human"],char:"\u{1f468}\u200d\u{1f393}",fitzpatrick_scale:!0,category:"people"},woman_singer:{keywords:["rockstar","entertainer","woman","human"],char:"\u{1f469}\u200d\u{1f3a4}",fitzpatrick_scale:!0,category:"people"},man_singer:{keywords:["rockstar","entertainer","man","human"],char:"\u{1f468}\u200d\u{1f3a4}",fitzpatrick_scale:!0,category:"people"},woman_teacher:{keywords:["instructor","professor","woman","human"],char:"\u{1f469}\u200d\u{1f3eb}",fitzpatrick_scale:!0,category:"people"},man_teacher:{keywords:["instructor","professor","man","human"],char:"\u{1f468}\u200d\u{1f3eb}",fitzpatrick_scale:!0,category:"people"},woman_factory_worker:{keywords:["assembly","industrial","woman","human"],char:"\u{1f469}\u200d\u{1f3ed}",fitzpatrick_scale:!0,category:"people"},man_factory_worker:{keywords:["assembly","industrial","man","human"],char:"\u{1f468}\u200d\u{1f3ed}",fitzpatrick_scale:!0,category:"people"},woman_technologist:{keywords:["coder","developer","engineer","programmer","software","woman","human","laptop","computer"],char:"\u{1f469}\u200d\u{1f4bb}",fitzpatrick_scale:!0,category:"people"},man_technologist:{keywords:["coder","developer","engineer","programmer","software","man","human","laptop","computer"],char:"\u{1f468}\u200d\u{1f4bb}",fitzpatrick_scale:!0,category:"people"},woman_office_worker:{keywords:["business","manager","woman","human"],char:"\u{1f469}\u200d\u{1f4bc}",fitzpatrick_scale:!0,category:"people"},man_office_worker:{keywords:["business","manager","man","human"],char:"\u{1f468}\u200d\u{1f4bc}",fitzpatrick_scale:!0,category:"people"},woman_mechanic:{keywords:["plumber","woman","human","wrench"],char:"\u{1f469}\u200d\u{1f527}",fitzpatrick_scale:!0,category:"people"},man_mechanic:{keywords:["plumber","man","human","wrench"],char:"\u{1f468}\u200d\u{1f527}",fitzpatrick_scale:!0,category:"people"},woman_scientist:{keywords:["biologist","chemist","engineer","physicist","woman","human"],char:"\u{1f469}\u200d\u{1f52c}",fitzpatrick_scale:!0,category:"people"},man_scientist:{keywords:["biologist","chemist","engineer","physicist","man","human"],char:"\u{1f468}\u200d\u{1f52c}",fitzpatrick_scale:!0,category:"people"},woman_artist:{keywords:["painter","woman","human"],char:"\u{1f469}\u200d\u{1f3a8}",fitzpatrick_scale:!0,category:"people"},man_artist:{keywords:["painter","man","human"],char:"\u{1f468}\u200d\u{1f3a8}",fitzpatrick_scale:!0,category:"people"},woman_firefighter:{keywords:["fireman","woman","human"],char:"\u{1f469}\u200d\u{1f692}",fitzpatrick_scale:!0,category:"people"},man_firefighter:{keywords:["fireman","man","human"],char:"\u{1f468}\u200d\u{1f692}",fitzpatrick_scale:!0,category:"people"},woman_pilot:{keywords:["aviator","plane","woman","human"],char:"\u{1f469}\u200d\u2708\ufe0f",fitzpatrick_scale:!0,category:"people"},man_pilot:{keywords:["aviator","plane","man","human"],char:"\u{1f468}\u200d\u2708\ufe0f",fitzpatrick_scale:!0,category:"people"},woman_astronaut:{keywords:["space","rocket","woman","human"],char:"\u{1f469}\u200d\u{1f680}",fitzpatrick_scale:!0,category:"people"},man_astronaut:{keywords:["space","rocket","man","human"],char:"\u{1f468}\u200d\u{1f680}",fitzpatrick_scale:!0,category:"people"},woman_judge:{keywords:["justice","court","woman","human"],char:"\u{1f469}\u200d\u2696\ufe0f",fitzpatrick_scale:!0,category:"people"},man_judge:{keywords:["justice","court","man","human"],char:"\u{1f468}\u200d\u2696\ufe0f",fitzpatrick_scale:!0,category:"people"},woman_superhero:{keywords:["woman","female","good","heroine","superpowers"],char:"\u{1f9b8}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"people"},man_superhero:{keywords:["man","male","good","hero","superpowers"],char:"\u{1f9b8}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"people"},woman_supervillain:{keywords:["woman","female","evil","bad","criminal","heroine","superpowers"],char:"\u{1f9b9}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"people"},man_supervillain:{keywords:["man","male","evil","bad","criminal","hero","superpowers"],char:"\u{1f9b9}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"people"},mrs_claus:{keywords:["woman","female","xmas","mother christmas"],char:"\u{1f936}",fitzpatrick_scale:!0,category:"people"},santa:{keywords:["festival","man","male","xmas","father christmas"],char:"\u{1f385}",fitzpatrick_scale:!0,category:"people"},sorceress:{keywords:["woman","female","mage","witch"],char:"\u{1f9d9}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"people"},wizard:{keywords:["man","male","mage","sorcerer"],char:"\u{1f9d9}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"people"},woman_elf:{keywords:["woman","female"],char:"\u{1f9dd}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"people"},man_elf:{keywords:["man","male"],char:"\u{1f9dd}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"people"},woman_vampire:{keywords:["woman","female"],char:"\u{1f9db}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"people"},man_vampire:{keywords:["man","male","dracula"],char:"\u{1f9db}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"people"},woman_zombie:{keywords:["woman","female","undead","walking dead"],char:"\u{1f9df}\u200d\u2640\ufe0f",fitzpatrick_scale:!1,category:"people"},man_zombie:{keywords:["man","male","dracula","undead","walking dead"],char:"\u{1f9df}\u200d\u2642\ufe0f",fitzpatrick_scale:!1,category:"people"},woman_genie:{keywords:["woman","female"],char:"\u{1f9de}\u200d\u2640\ufe0f",fitzpatrick_scale:!1,category:"people"},man_genie:{keywords:["man","male"],char:"\u{1f9de}\u200d\u2642\ufe0f",fitzpatrick_scale:!1,category:"people"},mermaid:{keywords:["woman","female","merwoman","ariel"],char:"\u{1f9dc}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"people"},merman:{keywords:["man","male","triton"],char:"\u{1f9dc}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"people"},woman_fairy:{keywords:["woman","female"],char:"\u{1f9da}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"people"},man_fairy:{keywords:["man","male"],char:"\u{1f9da}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"people"},angel:{keywords:["heaven","wings","halo"],char:"\u{1f47c}",fitzpatrick_scale:!0,category:"people"},pregnant_woman:{keywords:["baby"],char:"\u{1f930}",fitzpatrick_scale:!0,category:"people"},breastfeeding:{keywords:["nursing","baby"],char:"\u{1f931}",fitzpatrick_scale:!0,category:"people"},princess:{keywords:["girl","woman","female","blond","crown","royal","queen"],char:"\u{1f478}",fitzpatrick_scale:!0,category:"people"},prince:{keywords:["boy","man","male","crown","royal","king"],char:"\u{1f934}",fitzpatrick_scale:!0,category:"people"},bride_with_veil:{keywords:["couple","marriage","wedding","woman","bride"],char:"\u{1f470}",fitzpatrick_scale:!0,category:"people"},man_in_tuxedo:{keywords:["couple","marriage","wedding","groom"],char:"\u{1f935}",fitzpatrick_scale:!0,category:"people"},running_woman:{keywords:["woman","walking","exercise","race","running","female"],char:"\u{1f3c3}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"people"},running_man:{keywords:["man","walking","exercise","race","running"],char:"\u{1f3c3}",fitzpatrick_scale:!0,category:"people"},walking_woman:{keywords:["human","feet","steps","woman","female"],char:"\u{1f6b6}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"people"},walking_man:{keywords:["human","feet","steps"],char:"\u{1f6b6}",fitzpatrick_scale:!0,category:"people"},dancer:{keywords:["female","girl","woman","fun"],char:"\u{1f483}",fitzpatrick_scale:!0,category:"people"},man_dancing:{keywords:["male","boy","fun","dancer"],char:"\u{1f57a}",fitzpatrick_scale:!0,category:"people"},dancing_women:{keywords:["female","bunny","women","girls"],char:"\u{1f46f}",fitzpatrick_scale:!1,category:"people"},dancing_men:{keywords:["male","bunny","men","boys"],char:"\u{1f46f}\u200d\u2642\ufe0f",fitzpatrick_scale:!1,category:"people"},couple:{keywords:["pair","people","human","love","date","dating","like","affection","valentines","marriage"],char:"\u{1f46b}",fitzpatrick_scale:!1,category:"people"},two_men_holding_hands:{keywords:["pair","couple","love","like","bromance","friendship","people","human"],char:"\u{1f46c}",fitzpatrick_scale:!1,category:"people"},two_women_holding_hands:{keywords:["pair","friendship","couple","love","like","female","people","human"],char:"\u{1f46d}",fitzpatrick_scale:!1,category:"people"},bowing_woman:{keywords:["woman","female","girl"],char:"\u{1f647}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"people"},bowing_man:{keywords:["man","male","boy"],char:"\u{1f647}",fitzpatrick_scale:!0,category:"people"},man_facepalming:{keywords:["man","male","boy","disbelief"],char:"\u{1f926}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"people"},woman_facepalming:{keywords:["woman","female","girl","disbelief"],char:"\u{1f926}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"people"},woman_shrugging:{keywords:["woman","female","girl","confused","indifferent","doubt"],char:"\u{1f937}",fitzpatrick_scale:!0,category:"people"},man_shrugging:{keywords:["man","male","boy","confused","indifferent","doubt"],char:"\u{1f937}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"people"},tipping_hand_woman:{keywords:["female","girl","woman","human","information"],char:"\u{1f481}",fitzpatrick_scale:!0,category:"people"},tipping_hand_man:{keywords:["male","boy","man","human","information"],char:"\u{1f481}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"people"},no_good_woman:{keywords:["female","girl","woman","nope"],char:"\u{1f645}",fitzpatrick_scale:!0,category:"people"},no_good_man:{keywords:["male","boy","man","nope"],char:"\u{1f645}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"people"},ok_woman:{keywords:["women","girl","female","pink","human","woman"],char:"\u{1f646}",fitzpatrick_scale:!0,category:"people"},ok_man:{keywords:["men","boy","male","blue","human","man"],char:"\u{1f646}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"people"},raising_hand_woman:{keywords:["female","girl","woman"],char:"\u{1f64b}",fitzpatrick_scale:!0,category:"people"},raising_hand_man:{keywords:["male","boy","man"],char:"\u{1f64b}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"people"},pouting_woman:{keywords:["female","girl","woman"],char:"\u{1f64e}",fitzpatrick_scale:!0,category:"people"},pouting_man:{keywords:["male","boy","man"],char:"\u{1f64e}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"people"},frowning_woman:{keywords:["female","girl","woman","sad","depressed","discouraged","unhappy"],char:"\u{1f64d}",fitzpatrick_scale:!0,category:"people"},frowning_man:{keywords:["male","boy","man","sad","depressed","discouraged","unhappy"],char:"\u{1f64d}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"people"},haircut_woman:{keywords:["female","girl","woman"],char:"\u{1f487}",fitzpatrick_scale:!0,category:"people"},haircut_man:{keywords:["male","boy","man"],char:"\u{1f487}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"people"},massage_woman:{keywords:["female","girl","woman","head"],char:"\u{1f486}",fitzpatrick_scale:!0,category:"people"},massage_man:{keywords:["male","boy","man","head"],char:"\u{1f486}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"people"},woman_in_steamy_room:{keywords:["female","woman","spa","steamroom","sauna"],char:"\u{1f9d6}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"people"},man_in_steamy_room:{keywords:["male","man","spa","steamroom","sauna"],char:"\u{1f9d6}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"people"},couple_with_heart_woman_man:{keywords:["pair","love","like","affection","human","dating","valentines","marriage"],char:"\u{1f491}",fitzpatrick_scale:!1,category:"people"},couple_with_heart_woman_woman:{keywords:["pair","love","like","affection","human","dating","valentines","marriage"],char:"\u{1f469}\u200d\u2764\ufe0f\u200d\u{1f469}",fitzpatrick_scale:!1,category:"people"},couple_with_heart_man_man:{keywords:["pair","love","like","affection","human","dating","valentines","marriage"],char:"\u{1f468}\u200d\u2764\ufe0f\u200d\u{1f468}",fitzpatrick_scale:!1,category:"people"},couplekiss_man_woman:{keywords:["pair","valentines","love","like","dating","marriage"],char:"\u{1f48f}",fitzpatrick_scale:!1,category:"people"},couplekiss_woman_woman:{keywords:["pair","valentines","love","like","dating","marriage"],char:"\u{1f469}\u200d\u2764\ufe0f\u200d\u{1f48b}\u200d\u{1f469}",fitzpatrick_scale:!1,category:"people"},couplekiss_man_man:{keywords:["pair","valentines","love","like","dating","marriage"],char:"\u{1f468}\u200d\u2764\ufe0f\u200d\u{1f48b}\u200d\u{1f468}",fitzpatrick_scale:!1,category:"people"},family_man_woman_boy:{keywords:["home","parents","child","mom","dad","father","mother","people","human"],char:"\u{1f46a}",fitzpatrick_scale:!1,category:"people"},family_man_woman_girl:{keywords:["home","parents","people","human","child"],char:"\u{1f468}\u200d\u{1f469}\u200d\u{1f467}",fitzpatrick_scale:!1,category:"people"},family_man_woman_girl_boy:{keywords:["home","parents","people","human","children"],char:"\u{1f468}\u200d\u{1f469}\u200d\u{1f467}\u200d\u{1f466}",fitzpatrick_scale:!1,category:"people"},family_man_woman_boy_boy:{keywords:["home","parents","people","human","children"],char:"\u{1f468}\u200d\u{1f469}\u200d\u{1f466}\u200d\u{1f466}",fitzpatrick_scale:!1,category:"people"},family_man_woman_girl_girl:{keywords:["home","parents","people","human","children"],char:"\u{1f468}\u200d\u{1f469}\u200d\u{1f467}\u200d\u{1f467}",fitzpatrick_scale:!1,category:"people"},family_woman_woman_boy:{keywords:["home","parents","people","human","children"],char:"\u{1f469}\u200d\u{1f469}\u200d\u{1f466}",fitzpatrick_scale:!1,category:"people"},family_woman_woman_girl:{keywords:["home","parents","people","human","children"],char:"\u{1f469}\u200d\u{1f469}\u200d\u{1f467}",fitzpatrick_scale:!1,category:"people"},family_woman_woman_girl_boy:{keywords:["home","parents","people","human","children"],char:"\u{1f469}\u200d\u{1f469}\u200d\u{1f467}\u200d\u{1f466}",fitzpatrick_scale:!1,category:"people"},family_woman_woman_boy_boy:{keywords:["home","parents","people","human","children"],char:"\u{1f469}\u200d\u{1f469}\u200d\u{1f466}\u200d\u{1f466}",fitzpatrick_scale:!1,category:"people"},family_woman_woman_girl_girl:{keywords:["home","parents","people","human","children"],char:"\u{1f469}\u200d\u{1f469}\u200d\u{1f467}\u200d\u{1f467}",fitzpatrick_scale:!1,category:"people"},family_man_man_boy:{keywords:["home","parents","people","human","children"],char:"\u{1f468}\u200d\u{1f468}\u200d\u{1f466}",fitzpatrick_scale:!1,category:"people"},family_man_man_girl:{keywords:["home","parents","people","human","children"],char:"\u{1f468}\u200d\u{1f468}\u200d\u{1f467}",fitzpatrick_scale:!1,category:"people"},family_man_man_girl_boy:{keywords:["home","parents","people","human","children"],char:"\u{1f468}\u200d\u{1f468}\u200d\u{1f467}\u200d\u{1f466}",fitzpatrick_scale:!1,category:"people"},family_man_man_boy_boy:{keywords:["home","parents","people","human","children"],char:"\u{1f468}\u200d\u{1f468}\u200d\u{1f466}\u200d\u{1f466}",fitzpatrick_scale:!1,category:"people"},family_man_man_girl_girl:{keywords:["home","parents","people","human","children"],char:"\u{1f468}\u200d\u{1f468}\u200d\u{1f467}\u200d\u{1f467}",fitzpatrick_scale:!1,category:"people"},family_woman_boy:{keywords:["home","parent","people","human","child"],char:"\u{1f469}\u200d\u{1f466}",fitzpatrick_scale:!1,category:"people"},family_woman_girl:{keywords:["home","parent","people","human","child"],char:"\u{1f469}\u200d\u{1f467}",fitzpatrick_scale:!1,category:"people"},family_woman_girl_boy:{keywords:["home","parent","people","human","children"],char:"\u{1f469}\u200d\u{1f467}\u200d\u{1f466}",fitzpatrick_scale:!1,category:"people"},family_woman_boy_boy:{keywords:["home","parent","people","human","children"],char:"\u{1f469}\u200d\u{1f466}\u200d\u{1f466}",fitzpatrick_scale:!1,category:"people"},family_woman_girl_girl:{keywords:["home","parent","people","human","children"],char:"\u{1f469}\u200d\u{1f467}\u200d\u{1f467}",fitzpatrick_scale:!1,category:"people"},family_man_boy:{keywords:["home","parent","people","human","child"],char:"\u{1f468}\u200d\u{1f466}",fitzpatrick_scale:!1,category:"people"},family_man_girl:{keywords:["home","parent","people","human","child"],char:"\u{1f468}\u200d\u{1f467}",fitzpatrick_scale:!1,category:"people"},family_man_girl_boy:{keywords:["home","parent","people","human","children"],char:"\u{1f468}\u200d\u{1f467}\u200d\u{1f466}",fitzpatrick_scale:!1,category:"people"},family_man_boy_boy:{keywords:["home","parent","people","human","children"],char:"\u{1f468}\u200d\u{1f466}\u200d\u{1f466}",fitzpatrick_scale:!1,category:"people"},family_man_girl_girl:{keywords:["home","parent","people","human","children"],char:"\u{1f468}\u200d\u{1f467}\u200d\u{1f467}",fitzpatrick_scale:!1,category:"people"},yarn:{keywords:["ball","crochet","knit"],char:"\u{1f9f6}",fitzpatrick_scale:!1,category:"people"},thread:{keywords:["needle","sewing","spool","string"],char:"\u{1f9f5}",fitzpatrick_scale:!1,category:"people"},coat:{keywords:["jacket"],char:"\u{1f9e5}",fitzpatrick_scale:!1,category:"people"},labcoat:{keywords:["doctor","experiment","scientist","chemist"],char:"\u{1f97c}",fitzpatrick_scale:!1,category:"people"},womans_clothes:{keywords:["fashion","shopping_bags","female"],char:"\u{1f45a}",fitzpatrick_scale:!1,category:"people"},tshirt:{keywords:["fashion","cloth","casual","shirt","tee"],char:"\u{1f455}",fitzpatrick_scale:!1,category:"people"},jeans:{keywords:["fashion","shopping"],char:"\u{1f456}",fitzpatrick_scale:!1,category:"people"},necktie:{keywords:["shirt","suitup","formal","fashion","cloth","business"],char:"\u{1f454}",fitzpatrick_scale:!1,category:"people"},dress:{keywords:["clothes","fashion","shopping"],char:"\u{1f457}",fitzpatrick_scale:!1,category:"people"},bikini:{keywords:["swimming","female","woman","girl","fashion","beach","summer"],char:"\u{1f459}",fitzpatrick_scale:!1,category:"people"},kimono:{keywords:["dress","fashion","women","female","japanese"],char:"\u{1f458}",fitzpatrick_scale:!1,category:"people"},lipstick:{keywords:["female","girl","fashion","woman"],char:"\u{1f484}",fitzpatrick_scale:!1,category:"people"},kiss:{keywords:["face","lips","love","like","affection","valentines"],char:"\u{1f48b}",fitzpatrick_scale:!1,category:"people"},footprints:{keywords:["feet","tracking","walking","beach"],char:"\u{1f463}",fitzpatrick_scale:!1,category:"people"},flat_shoe:{keywords:["ballet","slip-on","slipper"],char:"\u{1f97f}",fitzpatrick_scale:!1,category:"people"},high_heel:{keywords:["fashion","shoes","female","pumps","stiletto"],char:"\u{1f460}",fitzpatrick_scale:!1,category:"people"},sandal:{keywords:["shoes","fashion","flip flops"],char:"\u{1f461}",fitzpatrick_scale:!1,category:"people"},boot:{keywords:["shoes","fashion"],char:"\u{1f462}",fitzpatrick_scale:!1,category:"people"},mans_shoe:{keywords:["fashion","male"],char:"\u{1f45e}",fitzpatrick_scale:!1,category:"people"},athletic_shoe:{keywords:["shoes","sports","sneakers"],char:"\u{1f45f}",fitzpatrick_scale:!1,category:"people"},hiking_boot:{keywords:["backpacking","camping","hiking"],char:"\u{1f97e}",fitzpatrick_scale:!1,category:"people"},socks:{keywords:["stockings","clothes"],char:"\u{1f9e6}",fitzpatrick_scale:!1,category:"people"},gloves:{keywords:["hands","winter","clothes"],char:"\u{1f9e4}",fitzpatrick_scale:!1,category:"people"},scarf:{keywords:["neck","winter","clothes"],char:"\u{1f9e3}",fitzpatrick_scale:!1,category:"people"},womans_hat:{keywords:["fashion","accessories","female","lady","spring"],char:"\u{1f452}",fitzpatrick_scale:!1,category:"people"},tophat:{keywords:["magic","gentleman","classy","circus"],char:"\u{1f3a9}",fitzpatrick_scale:!1,category:"people"},billed_hat:{keywords:["cap","baseball"],char:"\u{1f9e2}",fitzpatrick_scale:!1,category:"people"},rescue_worker_helmet:{keywords:["construction","build"],char:"\u26d1",fitzpatrick_scale:!1,category:"people"},mortar_board:{keywords:["school","college","degree","university","graduation","cap","hat","legal","learn","education"],char:"\u{1f393}",fitzpatrick_scale:!1,category:"people"},crown:{keywords:["king","kod","leader","royalty","lord"],char:"\u{1f451}",fitzpatrick_scale:!1,category:"people"},school_satchel:{keywords:["student","education","bag","backpack"],char:"\u{1f392}",fitzpatrick_scale:!1,category:"people"},luggage:{keywords:["packing","travel"],char:"\u{1f9f3}",fitzpatrick_scale:!1,category:"people"},pouch:{keywords:["bag","accessories","shopping"],char:"\u{1f45d}",fitzpatrick_scale:!1,category:"people"},purse:{keywords:["fashion","accessories","money","sales","shopping"],char:"\u{1f45b}",fitzpatrick_scale:!1,category:"people"},handbag:{keywords:["fashion","accessory","accessories","shopping"],char:"\u{1f45c}",fitzpatrick_scale:!1,category:"people"},briefcase:{keywords:["business","documents","work","law","legal","job","career"],char:"\u{1f4bc}",fitzpatrick_scale:!1,category:"people"},eyeglasses:{keywords:["fashion","accessories","eyesight","nerdy","dork","geek"],char:"\u{1f453}",fitzpatrick_scale:!1,category:"people"},dark_sunglasses:{keywords:["face","cool","accessories"],char:"\u{1f576}",fitzpatrick_scale:!1,category:"people"},goggles:{keywords:["eyes","protection","safety"],char:"\u{1f97d}",fitzpatrick_scale:!1,category:"people"},ring:{keywords:["wedding","propose","marriage","valentines","diamond","fashion","jewelry","gem","engagement"],char:"\u{1f48d}",fitzpatrick_scale:!1,category:"people"},closed_umbrella:{keywords:["weather","rain","drizzle"],char:"\u{1f302}",fitzpatrick_scale:!1,category:"people"},dog:{keywords:["animal","friend","nature","woof","puppy","pet","faithful"],char:"\u{1f436}",fitzpatrick_scale:!1,category:"animals_and_nature"},cat:{keywords:["animal","meow","nature","pet","kitten"],char:"\u{1f431}",fitzpatrick_scale:!1,category:"animals_and_nature"},mouse:{keywords:["animal","nature","cheese_wedge","rodent"],char:"\u{1f42d}",fitzpatrick_scale:!1,category:"animals_and_nature"},hamster:{keywords:["animal","nature"],char:"\u{1f439}",fitzpatrick_scale:!1,category:"animals_and_nature"},rabbit:{keywords:["animal","nature","pet","spring","magic","bunny"],char:"\u{1f430}",fitzpatrick_scale:!1,category:"animals_and_nature"},fox_face:{keywords:["animal","nature","face"],char:"\u{1f98a}",fitzpatrick_scale:!1,category:"animals_and_nature"},bear:{keywords:["animal","nature","wild"],char:"\u{1f43b}",fitzpatrick_scale:!1,category:"animals_and_nature"},panda_face:{keywords:["animal","nature","panda"],char:"\u{1f43c}",fitzpatrick_scale:!1,category:"animals_and_nature"},koala:{keywords:["animal","nature"],char:"\u{1f428}",fitzpatrick_scale:!1,category:"animals_and_nature"},tiger:{keywords:["animal","cat","danger","wild","nature","roar"],char:"\u{1f42f}",fitzpatrick_scale:!1,category:"animals_and_nature"},lion:{keywords:["animal","nature"],char:"\u{1f981}",fitzpatrick_scale:!1,category:"animals_and_nature"},cow:{keywords:["beef","ox","animal","nature","moo","milk"],char:"\u{1f42e}",fitzpatrick_scale:!1,category:"animals_and_nature"},pig:{keywords:["animal","oink","nature"],char:"\u{1f437}",fitzpatrick_scale:!1,category:"animals_and_nature"},pig_nose:{keywords:["animal","oink"],char:"\u{1f43d}",fitzpatrick_scale:!1,category:"animals_and_nature"},frog:{keywords:["animal","nature","croak","toad"],char:"\u{1f438}",fitzpatrick_scale:!1,category:"animals_and_nature"},squid:{keywords:["animal","nature","ocean","sea"],char:"\u{1f991}",fitzpatrick_scale:!1,category:"animals_and_nature"},octopus:{keywords:["animal","creature","ocean","sea","nature","beach"],char:"\u{1f419}",fitzpatrick_scale:!1,category:"animals_and_nature"},shrimp:{keywords:["animal","ocean","nature","seafood"],char:"\u{1f990}",fitzpatrick_scale:!1,category:"animals_and_nature"},monkey_face:{keywords:["animal","nature","circus"],char:"\u{1f435}",fitzpatrick_scale:!1,category:"animals_and_nature"},gorilla:{keywords:["animal","nature","circus"],char:"\u{1f98d}",fitzpatrick_scale:!1,category:"animals_and_nature"},see_no_evil:{keywords:["monkey","animal","nature","haha"],char:"\u{1f648}",fitzpatrick_scale:!1,category:"animals_and_nature"},hear_no_evil:{keywords:["animal","monkey","nature"],char:"\u{1f649}",fitzpatrick_scale:!1,category:"animals_and_nature"},speak_no_evil:{keywords:["monkey","animal","nature","omg"],char:"\u{1f64a}",fitzpatrick_scale:!1,category:"animals_and_nature"},monkey:{keywords:["animal","nature","banana","circus"],char:"\u{1f412}",fitzpatrick_scale:!1,category:"animals_and_nature"},chicken:{keywords:["animal","cluck","nature","bird"],char:"\u{1f414}",fitzpatrick_scale:!1,category:"animals_and_nature"},penguin:{keywords:["animal","nature"],char:"\u{1f427}",fitzpatrick_scale:!1,category:"animals_and_nature"},bird:{keywords:["animal","nature","fly","tweet","spring"],char:"\u{1f426}",fitzpatrick_scale:!1,category:"animals_and_nature"},baby_chick:{keywords:["animal","chicken","bird"],char:"\u{1f424}",fitzpatrick_scale:!1,category:"animals_and_nature"},hatching_chick:{keywords:["animal","chicken","egg","born","baby","bird"],char:"\u{1f423}",fitzpatrick_scale:!1,category:"animals_and_nature"},hatched_chick:{keywords:["animal","chicken","baby","bird"],char:"\u{1f425}",fitzpatrick_scale:!1,category:"animals_and_nature"},duck:{keywords:["animal","nature","bird","mallard"],char:"\u{1f986}",fitzpatrick_scale:!1,category:"animals_and_nature"},eagle:{keywords:["animal","nature","bird"],char:"\u{1f985}",fitzpatrick_scale:!1,category:"animals_and_nature"},owl:{keywords:["animal","nature","bird","hoot"],char:"\u{1f989}",fitzpatrick_scale:!1,category:"animals_and_nature"},bat:{keywords:["animal","nature","blind","vampire"],char:"\u{1f987}",fitzpatrick_scale:!1,category:"animals_and_nature"},wolf:{keywords:["animal","nature","wild"],char:"\u{1f43a}",fitzpatrick_scale:!1,category:"animals_and_nature"},boar:{keywords:["animal","nature"],char:"\u{1f417}",fitzpatrick_scale:!1,category:"animals_and_nature"},horse:{keywords:["animal","brown","nature"],char:"\u{1f434}",fitzpatrick_scale:!1,category:"animals_and_nature"},unicorn:{keywords:["animal","nature","mystical"],char:"\u{1f984}",fitzpatrick_scale:!1,category:"animals_and_nature"},honeybee:{keywords:["animal","insect","nature","bug","spring","honey"],char:"\u{1f41d}",fitzpatrick_scale:!1,category:"animals_and_nature"},bug:{keywords:["animal","insect","nature","worm"],char:"\u{1f41b}",fitzpatrick_scale:!1,category:"animals_and_nature"},butterfly:{keywords:["animal","insect","nature","caterpillar"],char:"\u{1f98b}",fitzpatrick_scale:!1,category:"animals_and_nature"},snail:{keywords:["slow","animal","shell"],char:"\u{1f40c}",fitzpatrick_scale:!1,category:"animals_and_nature"},beetle:{keywords:["animal","insect","nature","ladybug"],char:"\u{1f41e}",fitzpatrick_scale:!1,category:"animals_and_nature"},ant:{keywords:["animal","insect","nature","bug"],char:"\u{1f41c}",fitzpatrick_scale:!1,category:"animals_and_nature"},grasshopper:{keywords:["animal","cricket","chirp"],char:"\u{1f997}",fitzpatrick_scale:!1,category:"animals_and_nature"},spider:{keywords:["animal","arachnid"],char:"\u{1f577}",fitzpatrick_scale:!1,category:"animals_and_nature"},scorpion:{keywords:["animal","arachnid"],char:"\u{1f982}",fitzpatrick_scale:!1,category:"animals_and_nature"},crab:{keywords:["animal","crustacean"],char:"\u{1f980}",fitzpatrick_scale:!1,category:"animals_and_nature"},snake:{keywords:["animal","evil","nature","hiss","python"],char:"\u{1f40d}",fitzpatrick_scale:!1,category:"animals_and_nature"},lizard:{keywords:["animal","nature","reptile"],char:"\u{1f98e}",fitzpatrick_scale:!1,category:"animals_and_nature"},"t-rex":{keywords:["animal","nature","dinosaur","tyrannosaurus","extinct"],char:"\u{1f996}",fitzpatrick_scale:!1,category:"animals_and_nature"},sauropod:{keywords:["animal","nature","dinosaur","brachiosaurus","brontosaurus","diplodocus","extinct"],char:"\u{1f995}",fitzpatrick_scale:!1,category:"animals_and_nature"},turtle:{keywords:["animal","slow","nature","tortoise"],char:"\u{1f422}",fitzpatrick_scale:!1,category:"animals_and_nature"},tropical_fish:{keywords:["animal","swim","ocean","beach","nemo"],char:"\u{1f420}",fitzpatrick_scale:!1,category:"animals_and_nature"},fish:{keywords:["animal","food","nature"],char:"\u{1f41f}",fitzpatrick_scale:!1,category:"animals_and_nature"},blowfish:{keywords:["animal","nature","food","sea","ocean"],char:"\u{1f421}",fitzpatrick_scale:!1,category:"animals_and_nature"},dolphin:{keywords:["animal","nature","fish","sea","ocean","flipper","fins","beach"],char:"\u{1f42c}",fitzpatrick_scale:!1,category:"animals_and_nature"},shark:{keywords:["animal","nature","fish","sea","ocean","jaws","fins","beach"],char:"\u{1f988}",fitzpatrick_scale:!1,category:"animals_and_nature"},whale:{keywords:["animal","nature","sea","ocean"],char:"\u{1f433}",fitzpatrick_scale:!1,category:"animals_and_nature"},whale2:{keywords:["animal","nature","sea","ocean"],char:"\u{1f40b}",fitzpatrick_scale:!1,category:"animals_and_nature"},crocodile:{keywords:["animal","nature","reptile","lizard","alligator"],char:"\u{1f40a}",fitzpatrick_scale:!1,category:"animals_and_nature"},leopard:{keywords:["animal","nature"],char:"\u{1f406}",fitzpatrick_scale:!1,category:"animals_and_nature"},zebra:{keywords:["animal","nature","stripes","safari"],char:"\u{1f993}",fitzpatrick_scale:!1,category:"animals_and_nature"},tiger2:{keywords:["animal","nature","roar"],char:"\u{1f405}",fitzpatrick_scale:!1,category:"animals_and_nature"},water_buffalo:{keywords:["animal","nature","ox","cow"],char:"\u{1f403}",fitzpatrick_scale:!1,category:"animals_and_nature"},ox:{keywords:["animal","cow","beef"],char:"\u{1f402}",fitzpatrick_scale:!1,category:"animals_and_nature"},cow2:{keywords:["beef","ox","animal","nature","moo","milk"],char:"\u{1f404}",fitzpatrick_scale:!1,category:"animals_and_nature"},deer:{keywords:["animal","nature","horns","venison"],char:"\u{1f98c}",fitzpatrick_scale:!1,category:"animals_and_nature"},dromedary_camel:{keywords:["animal","hot","desert","hump"],char:"\u{1f42a}",fitzpatrick_scale:!1,category:"animals_and_nature"},camel:{keywords:["animal","nature","hot","desert","hump"],char:"\u{1f42b}",fitzpatrick_scale:!1,category:"animals_and_nature"},giraffe:{keywords:["animal","nature","spots","safari"],char:"\u{1f992}",fitzpatrick_scale:!1,category:"animals_and_nature"},elephant:{keywords:["animal","nature","nose","th","circus"],char:"\u{1f418}",fitzpatrick_scale:!1,category:"animals_and_nature"},rhinoceros:{keywords:["animal","nature","horn"],char:"\u{1f98f}",fitzpatrick_scale:!1,category:"animals_and_nature"},goat:{keywords:["animal","nature"],char:"\u{1f410}",fitzpatrick_scale:!1,category:"animals_and_nature"},ram:{keywords:["animal","sheep","nature"],char:"\u{1f40f}",fitzpatrick_scale:!1,category:"animals_and_nature"},sheep:{keywords:["animal","nature","wool","shipit"],char:"\u{1f411}",fitzpatrick_scale:!1,category:"animals_and_nature"},racehorse:{keywords:["animal","gamble","luck"],char:"\u{1f40e}",fitzpatrick_scale:!1,category:"animals_and_nature"},pig2:{keywords:["animal","nature"],char:"\u{1f416}",fitzpatrick_scale:!1,category:"animals_and_nature"},rat:{keywords:["animal","mouse","rodent"],char:"\u{1f400}",fitzpatrick_scale:!1,category:"animals_and_nature"},mouse2:{keywords:["animal","nature","rodent"],char:"\u{1f401}",fitzpatrick_scale:!1,category:"animals_and_nature"},rooster:{keywords:["animal","nature","chicken"],char:"\u{1f413}",fitzpatrick_scale:!1,category:"animals_and_nature"},turkey:{keywords:["animal","bird"],char:"\u{1f983}",fitzpatrick_scale:!1,category:"animals_and_nature"},dove:{keywords:["animal","bird"],char:"\u{1f54a}",fitzpatrick_scale:!1,category:"animals_and_nature"},dog2:{keywords:["animal","nature","friend","doge","pet","faithful"],char:"\u{1f415}",fitzpatrick_scale:!1,category:"animals_and_nature"},poodle:{keywords:["dog","animal","101","nature","pet"],char:"\u{1f429}",fitzpatrick_scale:!1,category:"animals_and_nature"},cat2:{keywords:["animal","meow","pet","cats"],char:"\u{1f408}",fitzpatrick_scale:!1,category:"animals_and_nature"},rabbit2:{keywords:["animal","nature","pet","magic","spring"],char:"\u{1f407}",fitzpatrick_scale:!1,category:"animals_and_nature"},chipmunk:{keywords:["animal","nature","rodent","squirrel"],char:"\u{1f43f}",fitzpatrick_scale:!1,category:"animals_and_nature"},hedgehog:{keywords:["animal","nature","spiny"],char:"\u{1f994}",fitzpatrick_scale:!1,category:"animals_and_nature"},raccoon:{keywords:["animal","nature"],char:"\u{1f99d}",fitzpatrick_scale:!1,category:"animals_and_nature"},llama:{keywords:["animal","nature","alpaca"],char:"\u{1f999}",fitzpatrick_scale:!1,category:"animals_and_nature"},hippopotamus:{keywords:["animal","nature"],char:"\u{1f99b}",fitzpatrick_scale:!1,category:"animals_and_nature"},kangaroo:{keywords:["animal","nature","australia","joey","hop","marsupial"],char:"\u{1f998}",fitzpatrick_scale:!1,category:"animals_and_nature"},badger:{keywords:["animal","nature","honey"],char:"\u{1f9a1}",fitzpatrick_scale:!1,category:"animals_and_nature"},swan:{keywords:["animal","nature","bird"],char:"\u{1f9a2}",fitzpatrick_scale:!1,category:"animals_and_nature"},peacock:{keywords:["animal","nature","peahen","bird"],char:"\u{1f99a}",fitzpatrick_scale:!1,category:"animals_and_nature"},parrot:{keywords:["animal","nature","bird","pirate","talk"],char:"\u{1f99c}",fitzpatrick_scale:!1,category:"animals_and_nature"},lobster:{keywords:["animal","nature","bisque","claws","seafood"],char:"\u{1f99e}",fitzpatrick_scale:!1,category:"animals_and_nature"},mosquito:{keywords:["animal","nature","insect","malaria"],char:"\u{1f99f}",fitzpatrick_scale:!1,category:"animals_and_nature"},paw_prints:{keywords:["animal","tracking","footprints","dog","cat","pet","feet"],char:"\u{1f43e}",fitzpatrick_scale:!1,category:"animals_and_nature"},dragon:{keywords:["animal","myth","nature","chinese","green"],char:"\u{1f409}",fitzpatrick_scale:!1,category:"animals_and_nature"},dragon_face:{keywords:["animal","myth","nature","chinese","green"],char:"\u{1f432}",fitzpatrick_scale:!1,category:"animals_and_nature"},cactus:{keywords:["vegetable","plant","nature"],char:"\u{1f335}",fitzpatrick_scale:!1,category:"animals_and_nature"},christmas_tree:{keywords:["festival","vacation","december","xmas","celebration"],char:"\u{1f384}",fitzpatrick_scale:!1,category:"animals_and_nature"},evergreen_tree:{keywords:["plant","nature"],char:"\u{1f332}",fitzpatrick_scale:!1,category:"animals_and_nature"},deciduous_tree:{keywords:["plant","nature"],char:"\u{1f333}",fitzpatrick_scale:!1,category:"animals_and_nature"},palm_tree:{keywords:["plant","vegetable","nature","summer","beach","mojito","tropical"],char:"\u{1f334}",fitzpatrick_scale:!1,category:"animals_and_nature"},seedling:{keywords:["plant","nature","grass","lawn","spring"],char:"\u{1f331}",fitzpatrick_scale:!1,category:"animals_and_nature"},herb:{keywords:["vegetable","plant","medicine","weed","grass","lawn"],char:"\u{1f33f}",fitzpatrick_scale:!1,category:"animals_and_nature"},shamrock:{keywords:["vegetable","plant","nature","irish","clover"],char:"\u2618",fitzpatrick_scale:!1,category:"animals_and_nature"},four_leaf_clover:{keywords:["vegetable","plant","nature","lucky","irish"],char:"\u{1f340}",fitzpatrick_scale:!1,category:"animals_and_nature"},bamboo:{keywords:["plant","nature","vegetable","panda","pine_decoration"],char:"\u{1f38d}",fitzpatrick_scale:!1,category:"animals_and_nature"},tanabata_tree:{keywords:["plant","nature","branch","summer"],char:"\u{1f38b}",fitzpatrick_scale:!1,category:"animals_and_nature"},leaves:{keywords:["nature","plant","tree","vegetable","grass","lawn","spring"],char:"\u{1f343}",fitzpatrick_scale:!1,category:"animals_and_nature"},fallen_leaf:{keywords:["nature","plant","vegetable","leaves"],char:"\u{1f342}",fitzpatrick_scale:!1,category:"animals_and_nature"},maple_leaf:{keywords:["nature","plant","vegetable","ca","fall"],char:"\u{1f341}",fitzpatrick_scale:!1,category:"animals_and_nature"},ear_of_rice:{keywords:["nature","plant"],char:"\u{1f33e}",fitzpatrick_scale:!1,category:"animals_and_nature"},hibiscus:{keywords:["plant","vegetable","flowers","beach"],char:"\u{1f33a}",fitzpatrick_scale:!1,category:"animals_and_nature"},sunflower:{keywords:["nature","plant","fall"],char:"\u{1f33b}",fitzpatrick_scale:!1,category:"animals_and_nature"},rose:{keywords:["flowers","valentines","love","spring"],char:"\u{1f339}",fitzpatrick_scale:!1,category:"animals_and_nature"},wilted_flower:{keywords:["plant","nature","flower"],char:"\u{1f940}",fitzpatrick_scale:!1,category:"animals_and_nature"},tulip:{keywords:["flowers","plant","nature","summer","spring"],char:"\u{1f337}",fitzpatrick_scale:!1,category:"animals_and_nature"},blossom:{keywords:["nature","flowers","yellow"],char:"\u{1f33c}",fitzpatrick_scale:!1,category:"animals_and_nature"},cherry_blossom:{keywords:["nature","plant","spring","flower"],char:"\u{1f338}",fitzpatrick_scale:!1,category:"animals_and_nature"},bouquet:{keywords:["flowers","nature","spring"],char:"\u{1f490}",fitzpatrick_scale:!1,category:"animals_and_nature"},mushroom:{keywords:["plant","vegetable"],char:"\u{1f344}",fitzpatrick_scale:!1,category:"animals_and_nature"},chestnut:{keywords:["food","squirrel"],char:"\u{1f330}",fitzpatrick_scale:!1,category:"animals_and_nature"},jack_o_lantern:{keywords:["halloween","light","pumpkin","creepy","fall"],char:"\u{1f383}",fitzpatrick_scale:!1,category:"animals_and_nature"},shell:{keywords:["nature","sea","beach"],char:"\u{1f41a}",fitzpatrick_scale:!1,category:"animals_and_nature"},spider_web:{keywords:["animal","insect","arachnid","silk"],char:"\u{1f578}",fitzpatrick_scale:!1,category:"animals_and_nature"},earth_americas:{keywords:["globe","world","USA","international"],char:"\u{1f30e}",fitzpatrick_scale:!1,category:"animals_and_nature"},earth_africa:{keywords:["globe","world","international"],char:"\u{1f30d}",fitzpatrick_scale:!1,category:"animals_and_nature"},earth_asia:{keywords:["globe","world","east","international"],char:"\u{1f30f}",fitzpatrick_scale:!1,category:"animals_and_nature"},full_moon:{keywords:["nature","yellow","twilight","planet","space","night","evening","sleep"],char:"\u{1f315}",fitzpatrick_scale:!1,category:"animals_and_nature"},waning_gibbous_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep","waxing_gibbous_moon"],char:"\u{1f316}",fitzpatrick_scale:!1,category:"animals_and_nature"},last_quarter_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"\u{1f317}",fitzpatrick_scale:!1,category:"animals_and_nature"},waning_crescent_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"\u{1f318}",fitzpatrick_scale:!1,category:"animals_and_nature"},new_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"\u{1f311}",fitzpatrick_scale:!1,category:"animals_and_nature"},waxing_crescent_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"\u{1f312}",fitzpatrick_scale:!1,category:"animals_and_nature"},first_quarter_moon:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"\u{1f313}",fitzpatrick_scale:!1,category:"animals_and_nature"},waxing_gibbous_moon:{keywords:["nature","night","sky","gray","twilight","planet","space","evening","sleep"],char:"\u{1f314}",fitzpatrick_scale:!1,category:"animals_and_nature"},new_moon_with_face:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"\u{1f31a}",fitzpatrick_scale:!1,category:"animals_and_nature"},full_moon_with_face:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"\u{1f31d}",fitzpatrick_scale:!1,category:"animals_and_nature"},first_quarter_moon_with_face:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"\u{1f31b}",fitzpatrick_scale:!1,category:"animals_and_nature"},last_quarter_moon_with_face:{keywords:["nature","twilight","planet","space","night","evening","sleep"],char:"\u{1f31c}",fitzpatrick_scale:!1,category:"animals_and_nature"},sun_with_face:{keywords:["nature","morning","sky"],char:"\u{1f31e}",fitzpatrick_scale:!1,category:"animals_and_nature"},crescent_moon:{keywords:["night","sleep","sky","evening","magic"],char:"\u{1f319}",fitzpatrick_scale:!1,category:"animals_and_nature"},star:{keywords:["night","yellow"],char:"\u2b50",fitzpatrick_scale:!1,category:"animals_and_nature"},star2:{keywords:["night","sparkle","awesome","good","magic"],char:"\u{1f31f}",fitzpatrick_scale:!1,category:"animals_and_nature"},dizzy:{keywords:["star","sparkle","shoot","magic"],char:"\u{1f4ab}",fitzpatrick_scale:!1,category:"animals_and_nature"},sparkles:{keywords:["stars","shine","shiny","cool","awesome","good","magic"],char:"\u2728",fitzpatrick_scale:!1,category:"animals_and_nature"},comet:{keywords:["space"],char:"\u2604",fitzpatrick_scale:!1,category:"animals_and_nature"},sunny:{keywords:["weather","nature","brightness","summer","beach","spring"],char:"\u2600\ufe0f",fitzpatrick_scale:!1,category:"animals_and_nature"},sun_behind_small_cloud:{keywords:["weather"],char:"\u{1f324}",fitzpatrick_scale:!1,category:"animals_and_nature"},partly_sunny:{keywords:["weather","nature","cloudy","morning","fall","spring"],char:"\u26c5",fitzpatrick_scale:!1,category:"animals_and_nature"},sun_behind_large_cloud:{keywords:["weather"],char:"\u{1f325}",fitzpatrick_scale:!1,category:"animals_and_nature"},sun_behind_rain_cloud:{keywords:["weather"],char:"\u{1f326}",fitzpatrick_scale:!1,category:"animals_and_nature"},cloud:{keywords:["weather","sky"],char:"\u2601\ufe0f",fitzpatrick_scale:!1,category:"animals_and_nature"},cloud_with_rain:{keywords:["weather"],char:"\u{1f327}",fitzpatrick_scale:!1,category:"animals_and_nature"},cloud_with_lightning_and_rain:{keywords:["weather","lightning"],char:"\u26c8",fitzpatrick_scale:!1,category:"animals_and_nature"},cloud_with_lightning:{keywords:["weather","thunder"],char:"\u{1f329}",fitzpatrick_scale:!1,category:"animals_and_nature"},zap:{keywords:["thunder","weather","lightning bolt","fast"],char:"\u26a1",fitzpatrick_scale:!1,category:"animals_and_nature"},fire:{keywords:["hot","cook","flame"],char:"\u{1f525}",fitzpatrick_scale:!1,category:"animals_and_nature"},boom:{keywords:["bomb","explode","explosion","collision","blown"],char:"\u{1f4a5}",fitzpatrick_scale:!1,category:"animals_and_nature"},snowflake:{keywords:["winter","season","cold","weather","christmas","xmas"],char:"\u2744\ufe0f",fitzpatrick_scale:!1,category:"animals_and_nature"},cloud_with_snow:{keywords:["weather"],char:"\u{1f328}",fitzpatrick_scale:!1,category:"animals_and_nature"},snowman:{keywords:["winter","season","cold","weather","christmas","xmas","frozen","without_snow"],char:"\u26c4",fitzpatrick_scale:!1,category:"animals_and_nature"},snowman_with_snow:{keywords:["winter","season","cold","weather","christmas","xmas","frozen"],char:"\u2603",fitzpatrick_scale:!1,category:"animals_and_nature"},wind_face:{keywords:["gust","air"],char:"\u{1f32c}",fitzpatrick_scale:!1,category:"animals_and_nature"},dash:{keywords:["wind","air","fast","shoo","fart","smoke","puff"],char:"\u{1f4a8}",fitzpatrick_scale:!1,category:"animals_and_nature"},tornado:{keywords:["weather","cyclone","twister"],char:"\u{1f32a}",fitzpatrick_scale:!1,category:"animals_and_nature"},fog:{keywords:["weather"],char:"\u{1f32b}",fitzpatrick_scale:!1,category:"animals_and_nature"},open_umbrella:{keywords:["weather","spring"],char:"\u2602",fitzpatrick_scale:!1,category:"animals_and_nature"},umbrella:{keywords:["rainy","weather","spring"],char:"\u2614",fitzpatrick_scale:!1,category:"animals_and_nature"},droplet:{keywords:["water","drip","faucet","spring"],char:"\u{1f4a7}",fitzpatrick_scale:!1,category:"animals_and_nature"},sweat_drops:{keywords:["water","drip","oops"],char:"\u{1f4a6}",fitzpatrick_scale:!1,category:"animals_and_nature"},ocean:{keywords:["sea","water","wave","nature","tsunami","disaster"],char:"\u{1f30a}",fitzpatrick_scale:!1,category:"animals_and_nature"},green_apple:{keywords:["fruit","nature"],char:"\u{1f34f}",fitzpatrick_scale:!1,category:"food_and_drink"},apple:{keywords:["fruit","mac","school"],char:"\u{1f34e}",fitzpatrick_scale:!1,category:"food_and_drink"},pear:{keywords:["fruit","nature","food"],char:"\u{1f350}",fitzpatrick_scale:!1,category:"food_and_drink"},tangerine:{keywords:["food","fruit","nature","orange"],char:"\u{1f34a}",fitzpatrick_scale:!1,category:"food_and_drink"},lemon:{keywords:["fruit","nature"],char:"\u{1f34b}",fitzpatrick_scale:!1,category:"food_and_drink"},banana:{keywords:["fruit","food","monkey"],char:"\u{1f34c}",fitzpatrick_scale:!1,category:"food_and_drink"},watermelon:{keywords:["fruit","food","picnic","summer"],char:"\u{1f349}",fitzpatrick_scale:!1,category:"food_and_drink"},grapes:{keywords:["fruit","food","wine"],char:"\u{1f347}",fitzpatrick_scale:!1,category:"food_and_drink"},strawberry:{keywords:["fruit","food","nature"],char:"\u{1f353}",fitzpatrick_scale:!1,category:"food_and_drink"},melon:{keywords:["fruit","nature","food"],char:"\u{1f348}",fitzpatrick_scale:!1,category:"food_and_drink"},cherries:{keywords:["food","fruit"],char:"\u{1f352}",fitzpatrick_scale:!1,category:"food_and_drink"},peach:{keywords:["fruit","nature","food"],char:"\u{1f351}",fitzpatrick_scale:!1,category:"food_and_drink"},pineapple:{keywords:["fruit","nature","food"],char:"\u{1f34d}",fitzpatrick_scale:!1,category:"food_and_drink"},coconut:{keywords:["fruit","nature","food","palm"],char:"\u{1f965}",fitzpatrick_scale:!1,category:"food_and_drink"},kiwi_fruit:{keywords:["fruit","food"],char:"\u{1f95d}",fitzpatrick_scale:!1,category:"food_and_drink"},mango:{keywords:["fruit","food","tropical"],char:"\u{1f96d}",fitzpatrick_scale:!1,category:"food_and_drink"},avocado:{keywords:["fruit","food"],char:"\u{1f951}",fitzpatrick_scale:!1,category:"food_and_drink"},broccoli:{keywords:["fruit","food","vegetable"],char:"\u{1f966}",fitzpatrick_scale:!1,category:"food_and_drink"},tomato:{keywords:["fruit","vegetable","nature","food"],char:"\u{1f345}",fitzpatrick_scale:!1,category:"food_and_drink"},eggplant:{keywords:["vegetable","nature","food","aubergine"],char:"\u{1f346}",fitzpatrick_scale:!1,category:"food_and_drink"},cucumber:{keywords:["fruit","food","pickle"],char:"\u{1f952}",fitzpatrick_scale:!1,category:"food_and_drink"},carrot:{keywords:["vegetable","food","orange"],char:"\u{1f955}",fitzpatrick_scale:!1,category:"food_and_drink"},hot_pepper:{keywords:["food","spicy","chilli","chili"],char:"\u{1f336}",fitzpatrick_scale:!1,category:"food_and_drink"},potato:{keywords:["food","tuber","vegatable","starch"],char:"\u{1f954}",fitzpatrick_scale:!1,category:"food_and_drink"},corn:{keywords:["food","vegetable","plant"],char:"\u{1f33d}",fitzpatrick_scale:!1,category:"food_and_drink"},leafy_greens:{keywords:["food","vegetable","plant","bok choy","cabbage","kale","lettuce"],char:"\u{1f96c}",fitzpatrick_scale:!1,category:"food_and_drink"},sweet_potato:{keywords:["food","nature"],char:"\u{1f360}",fitzpatrick_scale:!1,category:"food_and_drink"},peanuts:{keywords:["food","nut"],char:"\u{1f95c}",fitzpatrick_scale:!1,category:"food_and_drink"},honey_pot:{keywords:["bees","sweet","kitchen"],char:"\u{1f36f}",fitzpatrick_scale:!1,category:"food_and_drink"},croissant:{keywords:["food","bread","french"],char:"\u{1f950}",fitzpatrick_scale:!1,category:"food_and_drink"},bread:{keywords:["food","wheat","breakfast","toast"],char:"\u{1f35e}",fitzpatrick_scale:!1,category:"food_and_drink"},baguette_bread:{keywords:["food","bread","french"],char:"\u{1f956}",fitzpatrick_scale:!1,category:"food_and_drink"},bagel:{keywords:["food","bread","bakery","schmear"],char:"\u{1f96f}",fitzpatrick_scale:!1,category:"food_and_drink"},pretzel:{keywords:["food","bread","twisted"],char:"\u{1f968}",fitzpatrick_scale:!1,category:"food_and_drink"},cheese:{keywords:["food","chadder"],char:"\u{1f9c0}",fitzpatrick_scale:!1,category:"food_and_drink"},egg:{keywords:["food","chicken","breakfast"],char:"\u{1f95a}",fitzpatrick_scale:!1,category:"food_and_drink"},bacon:{keywords:["food","breakfast","pork","pig","meat"],char:"\u{1f953}",fitzpatrick_scale:!1,category:"food_and_drink"},steak:{keywords:["food","cow","meat","cut","chop","lambchop","porkchop"],char:"\u{1f969}",fitzpatrick_scale:!1,category:"food_and_drink"},pancakes:{keywords:["food","breakfast","flapjacks","hotcakes"],char:"\u{1f95e}",fitzpatrick_scale:!1,category:"food_and_drink"},poultry_leg:{keywords:["food","meat","drumstick","bird","chicken","turkey"],char:"\u{1f357}",fitzpatrick_scale:!1,category:"food_and_drink"},meat_on_bone:{keywords:["good","food","drumstick"],char:"\u{1f356}",fitzpatrick_scale:!1,category:"food_and_drink"},bone:{keywords:["skeleton"],char:"\u{1f9b4}",fitzpatrick_scale:!1,category:"food_and_drink"},fried_shrimp:{keywords:["food","animal","appetizer","summer"],char:"\u{1f364}",fitzpatrick_scale:!1,category:"food_and_drink"},fried_egg:{keywords:["food","breakfast","kitchen","egg"],char:"\u{1f373}",fitzpatrick_scale:!1,category:"food_and_drink"},hamburger:{keywords:["meat","fast food","beef","cheeseburger","mcdonalds","burger king"],char:"\u{1f354}",fitzpatrick_scale:!1,category:"food_and_drink"},fries:{keywords:["chips","snack","fast food"],char:"\u{1f35f}",fitzpatrick_scale:!1,category:"food_and_drink"},stuffed_flatbread:{keywords:["food","flatbread","stuffed","gyro"],char:"\u{1f959}",fitzpatrick_scale:!1,category:"food_and_drink"},hotdog:{keywords:["food","frankfurter"],char:"\u{1f32d}",fitzpatrick_scale:!1,category:"food_and_drink"},pizza:{keywords:["food","party"],char:"\u{1f355}",fitzpatrick_scale:!1,category:"food_and_drink"},sandwich:{keywords:["food","lunch","bread"],char:"\u{1f96a}",fitzpatrick_scale:!1,category:"food_and_drink"},canned_food:{keywords:["food","soup"],char:"\u{1f96b}",fitzpatrick_scale:!1,category:"food_and_drink"},spaghetti:{keywords:["food","italian","noodle"],char:"\u{1f35d}",fitzpatrick_scale:!1,category:"food_and_drink"},taco:{keywords:["food","mexican"],char:"\u{1f32e}",fitzpatrick_scale:!1,category:"food_and_drink"},burrito:{keywords:["food","mexican"],char:"\u{1f32f}",fitzpatrick_scale:!1,category:"food_and_drink"},green_salad:{keywords:["food","healthy","lettuce"],char:"\u{1f957}",fitzpatrick_scale:!1,category:"food_and_drink"},shallow_pan_of_food:{keywords:["food","cooking","casserole","paella"],char:"\u{1f958}",fitzpatrick_scale:!1,category:"food_and_drink"},ramen:{keywords:["food","japanese","noodle","chopsticks"],char:"\u{1f35c}",fitzpatrick_scale:!1,category:"food_and_drink"},stew:{keywords:["food","meat","soup"],char:"\u{1f372}",fitzpatrick_scale:!1,category:"food_and_drink"},fish_cake:{keywords:["food","japan","sea","beach","narutomaki","pink","swirl","kamaboko","surimi","ramen"],char:"\u{1f365}",fitzpatrick_scale:!1,category:"food_and_drink"},fortune_cookie:{keywords:["food","prophecy"],char:"\u{1f960}",fitzpatrick_scale:!1,category:"food_and_drink"},sushi:{keywords:["food","fish","japanese","rice"],char:"\u{1f363}",fitzpatrick_scale:!1,category:"food_and_drink"},bento:{keywords:["food","japanese","box"],char:"\u{1f371}",fitzpatrick_scale:!1,category:"food_and_drink"},curry:{keywords:["food","spicy","hot","indian"],char:"\u{1f35b}",fitzpatrick_scale:!1,category:"food_and_drink"},rice_ball:{keywords:["food","japanese"],char:"\u{1f359}",fitzpatrick_scale:!1,category:"food_and_drink"},rice:{keywords:["food","china","asian"],char:"\u{1f35a}",fitzpatrick_scale:!1,category:"food_and_drink"},rice_cracker:{keywords:["food","japanese"],char:"\u{1f358}",fitzpatrick_scale:!1,category:"food_and_drink"},oden:{keywords:["food","japanese"],char:"\u{1f362}",fitzpatrick_scale:!1,category:"food_and_drink"},dango:{keywords:["food","dessert","sweet","japanese","barbecue","meat"],char:"\u{1f361}",fitzpatrick_scale:!1,category:"food_and_drink"},shaved_ice:{keywords:["hot","dessert","summer"],char:"\u{1f367}",fitzpatrick_scale:!1,category:"food_and_drink"},ice_cream:{keywords:["food","hot","dessert"],char:"\u{1f368}",fitzpatrick_scale:!1,category:"food_and_drink"},icecream:{keywords:["food","hot","dessert","summer"],char:"\u{1f366}",fitzpatrick_scale:!1,category:"food_and_drink"},pie:{keywords:["food","dessert","pastry"],char:"\u{1f967}",fitzpatrick_scale:!1,category:"food_and_drink"},cake:{keywords:["food","dessert"],char:"\u{1f370}",fitzpatrick_scale:!1,category:"food_and_drink"},cupcake:{keywords:["food","dessert","bakery","sweet"],char:"\u{1f9c1}",fitzpatrick_scale:!1,category:"food_and_drink"},moon_cake:{keywords:["food","autumn"],char:"\u{1f96e}",fitzpatrick_scale:!1,category:"food_and_drink"},birthday:{keywords:["food","dessert","cake"],char:"\u{1f382}",fitzpatrick_scale:!1,category:"food_and_drink"},custard:{keywords:["dessert","food"],char:"\u{1f36e}",fitzpatrick_scale:!1,category:"food_and_drink"},candy:{keywords:["snack","dessert","sweet","lolly"],char:"\u{1f36c}",fitzpatrick_scale:!1,category:"food_and_drink"},lollipop:{keywords:["food","snack","candy","sweet"],char:"\u{1f36d}",fitzpatrick_scale:!1,category:"food_and_drink"},chocolate_bar:{keywords:["food","snack","dessert","sweet"],char:"\u{1f36b}",fitzpatrick_scale:!1,category:"food_and_drink"},popcorn:{keywords:["food","movie theater","films","snack"],char:"\u{1f37f}",fitzpatrick_scale:!1,category:"food_and_drink"},dumpling:{keywords:["food","empanada","pierogi","potsticker"],char:"\u{1f95f}",fitzpatrick_scale:!1,category:"food_and_drink"},doughnut:{keywords:["food","dessert","snack","sweet","donut"],char:"\u{1f369}",fitzpatrick_scale:!1,category:"food_and_drink"},cookie:{keywords:["food","snack","oreo","chocolate","sweet","dessert"],char:"\u{1f36a}",fitzpatrick_scale:!1,category:"food_and_drink"},milk_glass:{keywords:["beverage","drink","cow"],char:"\u{1f95b}",fitzpatrick_scale:!1,category:"food_and_drink"},beer:{keywords:["relax","beverage","drink","drunk","party","pub","summer","alcohol","booze"],char:"\u{1f37a}",fitzpatrick_scale:!1,category:"food_and_drink"},beers:{keywords:["relax","beverage","drink","drunk","party","pub","summer","alcohol","booze"],char:"\u{1f37b}",fitzpatrick_scale:!1,category:"food_and_drink"},clinking_glasses:{keywords:["beverage","drink","party","alcohol","celebrate","cheers","wine","champagne","toast"],char:"\u{1f942}",fitzpatrick_scale:!1,category:"food_and_drink"},wine_glass:{keywords:["drink","beverage","drunk","alcohol","booze"],char:"\u{1f377}",fitzpatrick_scale:!1,category:"food_and_drink"},tumbler_glass:{keywords:["drink","beverage","drunk","alcohol","liquor","booze","bourbon","scotch","whisky","glass","shot"],char:"\u{1f943}",fitzpatrick_scale:!1,category:"food_and_drink"},cocktail:{keywords:["drink","drunk","alcohol","beverage","booze","mojito"],char:"\u{1f378}",fitzpatrick_scale:!1,category:"food_and_drink"},tropical_drink:{keywords:["beverage","cocktail","summer","beach","alcohol","booze","mojito"],char:"\u{1f379}",fitzpatrick_scale:!1,category:"food_and_drink"},champagne:{keywords:["drink","wine","bottle","celebration"],char:"\u{1f37e}",fitzpatrick_scale:!1,category:"food_and_drink"},sake:{keywords:["wine","drink","drunk","beverage","japanese","alcohol","booze"],char:"\u{1f376}",fitzpatrick_scale:!1,category:"food_and_drink"},tea:{keywords:["drink","bowl","breakfast","green","british"],char:"\u{1f375}",fitzpatrick_scale:!1,category:"food_and_drink"},cup_with_straw:{keywords:["drink","soda"],char:"\u{1f964}",fitzpatrick_scale:!1,category:"food_and_drink"},coffee:{keywords:["beverage","caffeine","latte","espresso"],char:"\u2615",fitzpatrick_scale:!1,category:"food_and_drink"},baby_bottle:{keywords:["food","container","milk"],char:"\u{1f37c}",fitzpatrick_scale:!1,category:"food_and_drink"},salt:{keywords:["condiment","shaker"],char:"\u{1f9c2}",fitzpatrick_scale:!1,category:"food_and_drink"},spoon:{keywords:["cutlery","kitchen","tableware"],char:"\u{1f944}",fitzpatrick_scale:!1,category:"food_and_drink"},fork_and_knife:{keywords:["cutlery","kitchen"],char:"\u{1f374}",fitzpatrick_scale:!1,category:"food_and_drink"},plate_with_cutlery:{keywords:["food","eat","meal","lunch","dinner","restaurant"],char:"\u{1f37d}",fitzpatrick_scale:!1,category:"food_and_drink"},bowl_with_spoon:{keywords:["food","breakfast","cereal","oatmeal","porridge"],char:"\u{1f963}",fitzpatrick_scale:!1,category:"food_and_drink"},takeout_box:{keywords:["food","leftovers"],char:"\u{1f961}",fitzpatrick_scale:!1,category:"food_and_drink"},chopsticks:{keywords:["food"],char:"\u{1f962}",fitzpatrick_scale:!1,category:"food_and_drink"},soccer:{keywords:["sports","football"],char:"\u26bd",fitzpatrick_scale:!1,category:"activity"},basketball:{keywords:["sports","balls","NBA"],char:"\u{1f3c0}",fitzpatrick_scale:!1,category:"activity"},football:{keywords:["sports","balls","NFL"],char:"\u{1f3c8}",fitzpatrick_scale:!1,category:"activity"},baseball:{keywords:["sports","balls"],char:"\u26be",fitzpatrick_scale:!1,category:"activity"},softball:{keywords:["sports","balls"],char:"\u{1f94e}",fitzpatrick_scale:!1,category:"activity"},tennis:{keywords:["sports","balls","green"],char:"\u{1f3be}",fitzpatrick_scale:!1,category:"activity"},volleyball:{keywords:["sports","balls"],char:"\u{1f3d0}",fitzpatrick_scale:!1,category:"activity"},rugby_football:{keywords:["sports","team"],char:"\u{1f3c9}",fitzpatrick_scale:!1,category:"activity"},flying_disc:{keywords:["sports","frisbee","ultimate"],char:"\u{1f94f}",fitzpatrick_scale:!1,category:"activity"},"8ball":{keywords:["pool","hobby","game","luck","magic"],char:"\u{1f3b1}",fitzpatrick_scale:!1,category:"activity"},golf:{keywords:["sports","business","flag","hole","summer"],char:"\u26f3",fitzpatrick_scale:!1,category:"activity"},golfing_woman:{keywords:["sports","business","woman","female"],char:"\u{1f3cc}\ufe0f\u200d\u2640\ufe0f",fitzpatrick_scale:!1,category:"activity"},golfing_man:{keywords:["sports","business"],char:"\u{1f3cc}",fitzpatrick_scale:!0,category:"activity"},ping_pong:{keywords:["sports","pingpong"],char:"\u{1f3d3}",fitzpatrick_scale:!1,category:"activity"},badminton:{keywords:["sports"],char:"\u{1f3f8}",fitzpatrick_scale:!1,category:"activity"},goal_net:{keywords:["sports"],char:"\u{1f945}",fitzpatrick_scale:!1,category:"activity"},ice_hockey:{keywords:["sports"],char:"\u{1f3d2}",fitzpatrick_scale:!1,category:"activity"},field_hockey:{keywords:["sports"],char:"\u{1f3d1}",fitzpatrick_scale:!1,category:"activity"},lacrosse:{keywords:["sports","ball","stick"],char:"\u{1f94d}",fitzpatrick_scale:!1,category:"activity"},cricket:{keywords:["sports"],char:"\u{1f3cf}",fitzpatrick_scale:!1,category:"activity"},ski:{keywords:["sports","winter","cold","snow"],char:"\u{1f3bf}",fitzpatrick_scale:!1,category:"activity"},skier:{keywords:["sports","winter","snow"],char:"\u26f7",fitzpatrick_scale:!1,category:"activity"},snowboarder:{keywords:["sports","winter"],char:"\u{1f3c2}",fitzpatrick_scale:!0,category:"activity"},person_fencing:{keywords:["sports","fencing","sword"],char:"\u{1f93a}",fitzpatrick_scale:!1,category:"activity"},women_wrestling:{keywords:["sports","wrestlers"],char:"\u{1f93c}\u200d\u2640\ufe0f",fitzpatrick_scale:!1,category:"activity"},men_wrestling:{keywords:["sports","wrestlers"],char:"\u{1f93c}\u200d\u2642\ufe0f",fitzpatrick_scale:!1,category:"activity"},woman_cartwheeling:{keywords:["gymnastics"],char:"\u{1f938}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"activity"},man_cartwheeling:{keywords:["gymnastics"],char:"\u{1f938}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"activity"},woman_playing_handball:{keywords:["sports"],char:"\u{1f93e}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"activity"},man_playing_handball:{keywords:["sports"],char:"\u{1f93e}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"activity"},ice_skate:{keywords:["sports"],char:"\u26f8",fitzpatrick_scale:!1,category:"activity"},curling_stone:{keywords:["sports"],char:"\u{1f94c}",fitzpatrick_scale:!1,category:"activity"},skateboard:{keywords:["board"],char:"\u{1f6f9}",fitzpatrick_scale:!1,category:"activity"},sled:{keywords:["sleigh","luge","toboggan"],char:"\u{1f6f7}",fitzpatrick_scale:!1,category:"activity"},bow_and_arrow:{keywords:["sports"],char:"\u{1f3f9}",fitzpatrick_scale:!1,category:"activity"},fishing_pole_and_fish:{keywords:["food","hobby","summer"],char:"\u{1f3a3}",fitzpatrick_scale:!1,category:"activity"},boxing_glove:{keywords:["sports","fighting"],char:"\u{1f94a}",fitzpatrick_scale:!1,category:"activity"},martial_arts_uniform:{keywords:["judo","karate","taekwondo"],char:"\u{1f94b}",fitzpatrick_scale:!1,category:"activity"},rowing_woman:{keywords:["sports","hobby","water","ship","woman","female"],char:"\u{1f6a3}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"activity"},rowing_man:{keywords:["sports","hobby","water","ship"],char:"\u{1f6a3}",fitzpatrick_scale:!0,category:"activity"},climbing_woman:{keywords:["sports","hobby","woman","female","rock"],char:"\u{1f9d7}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"activity"},climbing_man:{keywords:["sports","hobby","man","male","rock"],char:"\u{1f9d7}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"activity"},swimming_woman:{keywords:["sports","exercise","human","athlete","water","summer","woman","female"],char:"\u{1f3ca}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"activity"},swimming_man:{keywords:["sports","exercise","human","athlete","water","summer"],char:"\u{1f3ca}",fitzpatrick_scale:!0,category:"activity"},woman_playing_water_polo:{keywords:["sports","pool"],char:"\u{1f93d}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"activity"},man_playing_water_polo:{keywords:["sports","pool"],char:"\u{1f93d}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"activity"},woman_in_lotus_position:{keywords:["woman","female","meditation","yoga","serenity","zen","mindfulness"],char:"\u{1f9d8}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"activity"},man_in_lotus_position:{keywords:["man","male","meditation","yoga","serenity","zen","mindfulness"],char:"\u{1f9d8}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"activity"},surfing_woman:{keywords:["sports","ocean","sea","summer","beach","woman","female"],char:"\u{1f3c4}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"activity"},surfing_man:{keywords:["sports","ocean","sea","summer","beach"],char:"\u{1f3c4}",fitzpatrick_scale:!0,category:"activity"},bath:{keywords:["clean","shower","bathroom"],char:"\u{1f6c0}",fitzpatrick_scale:!0,category:"activity"},basketball_woman:{keywords:["sports","human","woman","female"],char:"\u26f9\ufe0f\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"activity"},basketball_man:{keywords:["sports","human"],char:"\u26f9",fitzpatrick_scale:!0,category:"activity"},weight_lifting_woman:{keywords:["sports","training","exercise","woman","female"],char:"\u{1f3cb}\ufe0f\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"activity"},weight_lifting_man:{keywords:["sports","training","exercise"],char:"\u{1f3cb}",fitzpatrick_scale:!0,category:"activity"},biking_woman:{keywords:["sports","bike","exercise","hipster","woman","female"],char:"\u{1f6b4}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"activity"},biking_man:{keywords:["sports","bike","exercise","hipster"],char:"\u{1f6b4}",fitzpatrick_scale:!0,category:"activity"},mountain_biking_woman:{keywords:["transportation","sports","human","race","bike","woman","female"],char:"\u{1f6b5}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"activity"},mountain_biking_man:{keywords:["transportation","sports","human","race","bike"],char:"\u{1f6b5}",fitzpatrick_scale:!0,category:"activity"},horse_racing:{keywords:["animal","betting","competition","gambling","luck"],char:"\u{1f3c7}",fitzpatrick_scale:!0,category:"activity"},business_suit_levitating:{keywords:["suit","business","levitate","hover","jump"],char:"\u{1f574}",fitzpatrick_scale:!0,category:"activity"},trophy:{keywords:["win","award","contest","place","ftw","ceremony"],char:"\u{1f3c6}",fitzpatrick_scale:!1,category:"activity"},running_shirt_with_sash:{keywords:["play","pageant"],char:"\u{1f3bd}",fitzpatrick_scale:!1,category:"activity"},medal_sports:{keywords:["award","winning"],char:"\u{1f3c5}",fitzpatrick_scale:!1,category:"activity"},medal_military:{keywords:["award","winning","army"],char:"\u{1f396}",fitzpatrick_scale:!1,category:"activity"},"1st_place_medal":{keywords:["award","winning","first"],char:"\u{1f947}",fitzpatrick_scale:!1,category:"activity"},"2nd_place_medal":{keywords:["award","second"],char:"\u{1f948}",fitzpatrick_scale:!1,category:"activity"},"3rd_place_medal":{keywords:["award","third"],char:"\u{1f949}",fitzpatrick_scale:!1,category:"activity"},reminder_ribbon:{keywords:["sports","cause","support","awareness"],char:"\u{1f397}",fitzpatrick_scale:!1,category:"activity"},rosette:{keywords:["flower","decoration","military"],char:"\u{1f3f5}",fitzpatrick_scale:!1,category:"activity"},ticket:{keywords:["event","concert","pass"],char:"\u{1f3ab}",fitzpatrick_scale:!1,category:"activity"},tickets:{keywords:["sports","concert","entrance"],char:"\u{1f39f}",fitzpatrick_scale:!1,category:"activity"},performing_arts:{keywords:["acting","theater","drama"],char:"\u{1f3ad}",fitzpatrick_scale:!1,category:"activity"},art:{keywords:["design","paint","draw","colors"],char:"\u{1f3a8}",fitzpatrick_scale:!1,category:"activity"},circus_tent:{keywords:["festival","carnival","party"],char:"\u{1f3aa}",fitzpatrick_scale:!1,category:"activity"},woman_juggling:{keywords:["juggle","balance","skill","multitask"],char:"\u{1f939}\u200d\u2640\ufe0f",fitzpatrick_scale:!0,category:"activity"},man_juggling:{keywords:["juggle","balance","skill","multitask"],char:"\u{1f939}\u200d\u2642\ufe0f",fitzpatrick_scale:!0,category:"activity"},microphone:{keywords:["sound","music","PA","sing","talkshow"],char:"\u{1f3a4}",fitzpatrick_scale:!1,category:"activity"},headphones:{keywords:["music","score","gadgets"],char:"\u{1f3a7}",fitzpatrick_scale:!1,category:"activity"},musical_score:{keywords:["treble","clef","compose"],char:"\u{1f3bc}",fitzpatrick_scale:!1,category:"activity"},musical_keyboard:{keywords:["piano","instrument","compose"],char:"\u{1f3b9}",fitzpatrick_scale:!1,category:"activity"},drum:{keywords:["music","instrument","drumsticks","snare"],char:"\u{1f941}",fitzpatrick_scale:!1,category:"activity"},saxophone:{keywords:["music","instrument","jazz","blues"],char:"\u{1f3b7}",fitzpatrick_scale:!1,category:"activity"},trumpet:{keywords:["music","brass"],char:"\u{1f3ba}",fitzpatrick_scale:!1,category:"activity"},guitar:{keywords:["music","instrument"],char:"\u{1f3b8}",fitzpatrick_scale:!1,category:"activity"},violin:{keywords:["music","instrument","orchestra","symphony"],char:"\u{1f3bb}",fitzpatrick_scale:!1,category:"activity"},clapper:{keywords:["movie","film","record"],char:"\u{1f3ac}",fitzpatrick_scale:!1,category:"activity"},video_game:{keywords:["play","console","PS4","controller"],char:"\u{1f3ae}",fitzpatrick_scale:!1,category:"activity"},space_invader:{keywords:["game","arcade","play"],char:"\u{1f47e}",fitzpatrick_scale:!1,category:"activity"},dart:{keywords:["game","play","bar","target","bullseye"],char:"\u{1f3af}",fitzpatrick_scale:!1,category:"activity"},game_die:{keywords:["dice","random","tabletop","play","luck"],char:"\u{1f3b2}",fitzpatrick_scale:!1,category:"activity"},chess_pawn:{keywords:["expendable"],char:"\u265f",fitzpatrick_scale:!1,category:"activity"},slot_machine:{keywords:["bet","gamble","vegas","fruit machine","luck","casino"],char:"\u{1f3b0}",fitzpatrick_scale:!1,category:"activity"},jigsaw:{keywords:["interlocking","puzzle","piece"],char:"\u{1f9e9}",fitzpatrick_scale:!1,category:"activity"},bowling:{keywords:["sports","fun","play"],char:"\u{1f3b3}",fitzpatrick_scale:!1,category:"activity"},red_car:{keywords:["red","transportation","vehicle"],char:"\u{1f697}",fitzpatrick_scale:!1,category:"travel_and_places"},taxi:{keywords:["uber","vehicle","cars","transportation"],char:"\u{1f695}",fitzpatrick_scale:!1,category:"travel_and_places"},blue_car:{keywords:["transportation","vehicle"],char:"\u{1f699}",fitzpatrick_scale:!1,category:"travel_and_places"},bus:{keywords:["car","vehicle","transportation"],char:"\u{1f68c}",fitzpatrick_scale:!1,category:"travel_and_places"},trolleybus:{keywords:["bart","transportation","vehicle"],char:"\u{1f68e}",fitzpatrick_scale:!1,category:"travel_and_places"},racing_car:{keywords:["sports","race","fast","formula","f1"],char:"\u{1f3ce}",fitzpatrick_scale:!1,category:"travel_and_places"},police_car:{keywords:["vehicle","cars","transportation","law","legal","enforcement"],char:"\u{1f693}",fitzpatrick_scale:!1,category:"travel_and_places"},ambulance:{keywords:["health","911","hospital"],char:"\u{1f691}",fitzpatrick_scale:!1,category:"travel_and_places"},fire_engine:{keywords:["transportation","cars","vehicle"],char:"\u{1f692}",fitzpatrick_scale:!1,category:"travel_and_places"},minibus:{keywords:["vehicle","car","transportation"],char:"\u{1f690}",fitzpatrick_scale:!1,category:"travel_and_places"},truck:{keywords:["cars","transportation"],char:"\u{1f69a}",fitzpatrick_scale:!1,category:"travel_and_places"},articulated_lorry:{keywords:["vehicle","cars","transportation","express"],char:"\u{1f69b}",fitzpatrick_scale:!1,category:"travel_and_places"},tractor:{keywords:["vehicle","car","farming","agriculture"],char:"\u{1f69c}",fitzpatrick_scale:!1,category:"travel_and_places"},kick_scooter:{keywords:["vehicle","kick","razor"],char:"\u{1f6f4}",fitzpatrick_scale:!1,category:"travel_and_places"},motorcycle:{keywords:["race","sports","fast"],char:"\u{1f3cd}",fitzpatrick_scale:!1,category:"travel_and_places"},bike:{keywords:["sports","bicycle","exercise","hipster"],char:"\u{1f6b2}",fitzpatrick_scale:!1,category:"travel_and_places"},motor_scooter:{keywords:["vehicle","vespa","sasha"],char:"\u{1f6f5}",fitzpatrick_scale:!1,category:"travel_and_places"},rotating_light:{keywords:["police","ambulance","911","emergency","alert","error","pinged","law","legal"],char:"\u{1f6a8}",fitzpatrick_scale:!1,category:"travel_and_places"},oncoming_police_car:{keywords:["vehicle","law","legal","enforcement","911"],char:"\u{1f694}",fitzpatrick_scale:!1,category:"travel_and_places"},oncoming_bus:{keywords:["vehicle","transportation"],char:"\u{1f68d}",fitzpatrick_scale:!1,category:"travel_and_places"},oncoming_automobile:{keywords:["car","vehicle","transportation"],char:"\u{1f698}",fitzpatrick_scale:!1,category:"travel_and_places"},oncoming_taxi:{keywords:["vehicle","cars","uber"],char:"\u{1f696}",fitzpatrick_scale:!1,category:"travel_and_places"},aerial_tramway:{keywords:["transportation","vehicle","ski"],char:"\u{1f6a1}",fitzpatrick_scale:!1,category:"travel_and_places"},mountain_cableway:{keywords:["transportation","vehicle","ski"],char:"\u{1f6a0}",fitzpatrick_scale:!1,category:"travel_and_places"},suspension_railway:{keywords:["vehicle","transportation"],char:"\u{1f69f}",fitzpatrick_scale:!1,category:"travel_and_places"},railway_car:{keywords:["transportation","vehicle"],char:"\u{1f683}",fitzpatrick_scale:!1,category:"travel_and_places"},train:{keywords:["transportation","vehicle","carriage","public","travel"],char:"\u{1f68b}",fitzpatrick_scale:!1,category:"travel_and_places"},monorail:{keywords:["transportation","vehicle"],char:"\u{1f69d}",fitzpatrick_scale:!1,category:"travel_and_places"},bullettrain_side:{keywords:["transportation","vehicle"],char:"\u{1f684}",fitzpatrick_scale:!1,category:"travel_and_places"},bullettrain_front:{keywords:["transportation","vehicle","speed","fast","public","travel"],char:"\u{1f685}",fitzpatrick_scale:!1,category:"travel_and_places"},light_rail:{keywords:["transportation","vehicle"],char:"\u{1f688}",fitzpatrick_scale:!1,category:"travel_and_places"},mountain_railway:{keywords:["transportation","vehicle"],char:"\u{1f69e}",fitzpatrick_scale:!1,category:"travel_and_places"},steam_locomotive:{keywords:["transportation","vehicle","train"],char:"\u{1f682}",fitzpatrick_scale:!1,category:"travel_and_places"},train2:{keywords:["transportation","vehicle"],char:"\u{1f686}",fitzpatrick_scale:!1,category:"travel_and_places"},metro:{keywords:["transportation","blue-square","mrt","underground","tube"],char:"\u{1f687}",fitzpatrick_scale:!1,category:"travel_and_places"},tram:{keywords:["transportation","vehicle"],char:"\u{1f68a}",fitzpatrick_scale:!1,category:"travel_and_places"},station:{keywords:["transportation","vehicle","public"],char:"\u{1f689}",fitzpatrick_scale:!1,category:"travel_and_places"},flying_saucer:{keywords:["transportation","vehicle","ufo"],char:"\u{1f6f8}",fitzpatrick_scale:!1,category:"travel_and_places"},helicopter:{keywords:["transportation","vehicle","fly"],char:"\u{1f681}",fitzpatrick_scale:!1,category:"travel_and_places"},small_airplane:{keywords:["flight","transportation","fly","vehicle"],char:"\u{1f6e9}",fitzpatrick_scale:!1,category:"travel_and_places"},airplane:{keywords:["vehicle","transportation","flight","fly"],char:"\u2708\ufe0f",fitzpatrick_scale:!1,category:"travel_and_places"},flight_departure:{keywords:["airport","flight","landing"],char:"\u{1f6eb}",fitzpatrick_scale:!1,category:"travel_and_places"},flight_arrival:{keywords:["airport","flight","boarding"],char:"\u{1f6ec}",fitzpatrick_scale:!1,category:"travel_and_places"},sailboat:{keywords:["ship","summer","transportation","water","sailing"],char:"\u26f5",fitzpatrick_scale:!1,category:"travel_and_places"},motor_boat:{keywords:["ship"],char:"\u{1f6e5}",fitzpatrick_scale:!1,category:"travel_and_places"},speedboat:{keywords:["ship","transportation","vehicle","summer"],char:"\u{1f6a4}",fitzpatrick_scale:!1,category:"travel_and_places"},ferry:{keywords:["boat","ship","yacht"],char:"\u26f4",fitzpatrick_scale:!1,category:"travel_and_places"},passenger_ship:{keywords:["yacht","cruise","ferry"],char:"\u{1f6f3}",fitzpatrick_scale:!1,category:"travel_and_places"},rocket:{keywords:["launch","ship","staffmode","NASA","outer space","outer_space","fly"],char:"\u{1f680}",fitzpatrick_scale:!1,category:"travel_and_places"},artificial_satellite:{keywords:["communication","gps","orbit","spaceflight","NASA","ISS"],char:"\u{1f6f0}",fitzpatrick_scale:!1,category:"travel_and_places"},seat:{keywords:["sit","airplane","transport","bus","flight","fly"],char:"\u{1f4ba}",fitzpatrick_scale:!1,category:"travel_and_places"},canoe:{keywords:["boat","paddle","water","ship"],char:"\u{1f6f6}",fitzpatrick_scale:!1,category:"travel_and_places"},anchor:{keywords:["ship","ferry","sea","boat"],char:"\u2693",fitzpatrick_scale:!1,category:"travel_and_places"},construction:{keywords:["wip","progress","caution","warning"],char:"\u{1f6a7}",fitzpatrick_scale:!1,category:"travel_and_places"},fuelpump:{keywords:["gas station","petroleum"],char:"\u26fd",fitzpatrick_scale:!1,category:"travel_and_places"},busstop:{keywords:["transportation","wait"],char:"\u{1f68f}",fitzpatrick_scale:!1,category:"travel_and_places"},vertical_traffic_light:{keywords:["transportation","driving"],char:"\u{1f6a6}",fitzpatrick_scale:!1,category:"travel_and_places"},traffic_light:{keywords:["transportation","signal"],char:"\u{1f6a5}",fitzpatrick_scale:!1,category:"travel_and_places"},checkered_flag:{keywords:["contest","finishline","race","gokart"],char:"\u{1f3c1}",fitzpatrick_scale:!1,category:"travel_and_places"},ship:{keywords:["transportation","titanic","deploy"],char:"\u{1f6a2}",fitzpatrick_scale:!1,category:"travel_and_places"},ferris_wheel:{keywords:["photo","carnival","londoneye"],char:"\u{1f3a1}",fitzpatrick_scale:!1,category:"travel_and_places"},roller_coaster:{keywords:["carnival","playground","photo","fun"],char:"\u{1f3a2}",fitzpatrick_scale:!1,category:"travel_and_places"},carousel_horse:{keywords:["photo","carnival"],char:"\u{1f3a0}",fitzpatrick_scale:!1,category:"travel_and_places"},building_construction:{keywords:["wip","working","progress"],char:"\u{1f3d7}",fitzpatrick_scale:!1,category:"travel_and_places"},foggy:{keywords:["photo","mountain"],char:"\u{1f301}",fitzpatrick_scale:!1,category:"travel_and_places"},tokyo_tower:{keywords:["photo","japanese"],char:"\u{1f5fc}",fitzpatrick_scale:!1,category:"travel_and_places"},factory:{keywords:["building","industry","pollution","smoke"],char:"\u{1f3ed}",fitzpatrick_scale:!1,category:"travel_and_places"},fountain:{keywords:["photo","summer","water","fresh"],char:"\u26f2",fitzpatrick_scale:!1,category:"travel_and_places"},rice_scene:{keywords:["photo","japan","asia","tsukimi"],char:"\u{1f391}",fitzpatrick_scale:!1,category:"travel_and_places"},mountain:{keywords:["photo","nature","environment"],char:"\u26f0",fitzpatrick_scale:!1,category:"travel_and_places"},mountain_snow:{keywords:["photo","nature","environment","winter","cold"],char:"\u{1f3d4}",fitzpatrick_scale:!1,category:"travel_and_places"},mount_fuji:{keywords:["photo","mountain","nature","japanese"],char:"\u{1f5fb}",fitzpatrick_scale:!1,category:"travel_and_places"},volcano:{keywords:["photo","nature","disaster"],char:"\u{1f30b}",fitzpatrick_scale:!1,category:"travel_and_places"},japan:{keywords:["nation","country","japanese","asia"],char:"\u{1f5fe}",fitzpatrick_scale:!1,category:"travel_and_places"},camping:{keywords:["photo","outdoors","tent"],char:"\u{1f3d5}",fitzpatrick_scale:!1,category:"travel_and_places"},tent:{keywords:["photo","camping","outdoors"],char:"\u26fa",fitzpatrick_scale:!1,category:"travel_and_places"},national_park:{keywords:["photo","environment","nature"],char:"\u{1f3de}",fitzpatrick_scale:!1,category:"travel_and_places"},motorway:{keywords:["road","cupertino","interstate","highway"],char:"\u{1f6e3}",fitzpatrick_scale:!1,category:"travel_and_places"},railway_track:{keywords:["train","transportation"],char:"\u{1f6e4}",fitzpatrick_scale:!1,category:"travel_and_places"},sunrise:{keywords:["morning","view","vacation","photo"],char:"\u{1f305}",fitzpatrick_scale:!1,category:"travel_and_places"},sunrise_over_mountains:{keywords:["view","vacation","photo"],char:"\u{1f304}",fitzpatrick_scale:!1,category:"travel_and_places"},desert:{keywords:["photo","warm","saharah"],char:"\u{1f3dc}",fitzpatrick_scale:!1,category:"travel_and_places"},beach_umbrella:{keywords:["weather","summer","sunny","sand","mojito"],char:"\u{1f3d6}",fitzpatrick_scale:!1,category:"travel_and_places"},desert_island:{keywords:["photo","tropical","mojito"],char:"\u{1f3dd}",fitzpatrick_scale:!1,category:"travel_and_places"},city_sunrise:{keywords:["photo","good morning","dawn"],char:"\u{1f307}",fitzpatrick_scale:!1,category:"travel_and_places"},city_sunset:{keywords:["photo","evening","sky","buildings"],char:"\u{1f306}",fitzpatrick_scale:!1,category:"travel_and_places"},cityscape:{keywords:["photo","night life","urban"],char:"\u{1f3d9}",fitzpatrick_scale:!1,category:"travel_and_places"},night_with_stars:{keywords:["evening","city","downtown"],char:"\u{1f303}",fitzpatrick_scale:!1,category:"travel_and_places"},bridge_at_night:{keywords:["photo","sanfrancisco"],char:"\u{1f309}",fitzpatrick_scale:!1,category:"travel_and_places"},milky_way:{keywords:["photo","space","stars"],char:"\u{1f30c}",fitzpatrick_scale:!1,category:"travel_and_places"},stars:{keywords:["night","photo"],char:"\u{1f320}",fitzpatrick_scale:!1,category:"travel_and_places"},sparkler:{keywords:["stars","night","shine"],char:"\u{1f387}",fitzpatrick_scale:!1,category:"travel_and_places"},fireworks:{keywords:["photo","festival","carnival","congratulations"],char:"\u{1f386}",fitzpatrick_scale:!1,category:"travel_and_places"},rainbow:{keywords:["nature","happy","unicorn_face","photo","sky","spring"],char:"\u{1f308}",fitzpatrick_scale:!1,category:"travel_and_places"},houses:{keywords:["buildings","photo"],char:"\u{1f3d8}",fitzpatrick_scale:!1,category:"travel_and_places"},european_castle:{keywords:["building","royalty","history"],char:"\u{1f3f0}",fitzpatrick_scale:!1,category:"travel_and_places"},japanese_castle:{keywords:["photo","building"],char:"\u{1f3ef}",fitzpatrick_scale:!1,category:"travel_and_places"},stadium:{keywords:["photo","place","sports","concert","venue"],char:"\u{1f3df}",fitzpatrick_scale:!1,category:"travel_and_places"},statue_of_liberty:{keywords:["american","newyork"],char:"\u{1f5fd}",fitzpatrick_scale:!1,category:"travel_and_places"},house:{keywords:["building","home"],char:"\u{1f3e0}",fitzpatrick_scale:!1,category:"travel_and_places"},house_with_garden:{keywords:["home","plant","nature"],char:"\u{1f3e1}",fitzpatrick_scale:!1,category:"travel_and_places"},derelict_house:{keywords:["abandon","evict","broken","building"],char:"\u{1f3da}",fitzpatrick_scale:!1,category:"travel_and_places"},office:{keywords:["building","bureau","work"],char:"\u{1f3e2}",fitzpatrick_scale:!1,category:"travel_and_places"},department_store:{keywords:["building","shopping","mall"],char:"\u{1f3ec}",fitzpatrick_scale:!1,category:"travel_and_places"},post_office:{keywords:["building","envelope","communication"],char:"\u{1f3e3}",fitzpatrick_scale:!1,category:"travel_and_places"},european_post_office:{keywords:["building","email"],char:"\u{1f3e4}",fitzpatrick_scale:!1,category:"travel_and_places"},hospital:{keywords:["building","health","surgery","doctor"],char:"\u{1f3e5}",fitzpatrick_scale:!1,category:"travel_and_places"},bank:{keywords:["building","money","sales","cash","business","enterprise"],char:"\u{1f3e6}",fitzpatrick_scale:!1,category:"travel_and_places"},hotel:{keywords:["building","accomodation","checkin"],char:"\u{1f3e8}",fitzpatrick_scale:!1,category:"travel_and_places"},convenience_store:{keywords:["building","shopping","groceries"],char:"\u{1f3ea}",fitzpatrick_scale:!1,category:"travel_and_places"},school:{keywords:["building","student","education","learn","teach"],char:"\u{1f3eb}",fitzpatrick_scale:!1,category:"travel_and_places"},love_hotel:{keywords:["like","affection","dating"],char:"\u{1f3e9}",fitzpatrick_scale:!1,category:"travel_and_places"},wedding:{keywords:["love","like","affection","couple","marriage","bride","groom"],char:"\u{1f492}",fitzpatrick_scale:!1,category:"travel_and_places"},classical_building:{keywords:["art","culture","history"],char:"\u{1f3db}",fitzpatrick_scale:!1,category:"travel_and_places"},church:{keywords:["building","religion","christ"],char:"\u26ea",fitzpatrick_scale:!1,category:"travel_and_places"},mosque:{keywords:["islam","worship","minaret"],char:"\u{1f54c}",fitzpatrick_scale:!1,category:"travel_and_places"},synagogue:{keywords:["judaism","worship","temple","jewish"],char:"\u{1f54d}",fitzpatrick_scale:!1,category:"travel_and_places"},kaaba:{keywords:["mecca","mosque","islam"],char:"\u{1f54b}",fitzpatrick_scale:!1,category:"travel_and_places"},shinto_shrine:{keywords:["temple","japan","kyoto"],char:"\u26e9",fitzpatrick_scale:!1,category:"travel_and_places"},watch:{keywords:["time","accessories"],char:"\u231a",fitzpatrick_scale:!1,category:"objects"},iphone:{keywords:["technology","apple","gadgets","dial"],char:"\u{1f4f1}",fitzpatrick_scale:!1,category:"objects"},calling:{keywords:["iphone","incoming"],char:"\u{1f4f2}",fitzpatrick_scale:!1,category:"objects"},computer:{keywords:["technology","laptop","screen","display","monitor"],char:"\u{1f4bb}",fitzpatrick_scale:!1,category:"objects"},keyboard:{keywords:["technology","computer","type","input","text"],char:"\u2328",fitzpatrick_scale:!1,category:"objects"},desktop_computer:{keywords:["technology","computing","screen"],char:"\u{1f5a5}",fitzpatrick_scale:!1,category:"objects"},printer:{keywords:["paper","ink"],char:"\u{1f5a8}",fitzpatrick_scale:!1,category:"objects"},computer_mouse:{keywords:["click"],char:"\u{1f5b1}",fitzpatrick_scale:!1,category:"objects"},trackball:{keywords:["technology","trackpad"],char:"\u{1f5b2}",fitzpatrick_scale:!1,category:"objects"},joystick:{keywords:["game","play"],char:"\u{1f579}",fitzpatrick_scale:!1,category:"objects"},clamp:{keywords:["tool"],char:"\u{1f5dc}",fitzpatrick_scale:!1,category:"objects"},minidisc:{keywords:["technology","record","data","disk","90s"],char:"\u{1f4bd}",fitzpatrick_scale:!1,category:"objects"},floppy_disk:{keywords:["oldschool","technology","save","90s","80s"],char:"\u{1f4be}",fitzpatrick_scale:!1,category:"objects"},cd:{keywords:["technology","dvd","disk","disc","90s"],char:"\u{1f4bf}",fitzpatrick_scale:!1,category:"objects"},dvd:{keywords:["cd","disk","disc"],char:"\u{1f4c0}",fitzpatrick_scale:!1,category:"objects"},vhs:{keywords:["record","video","oldschool","90s","80s"],char:"\u{1f4fc}",fitzpatrick_scale:!1,category:"objects"},camera:{keywords:["gadgets","photography"],char:"\u{1f4f7}",fitzpatrick_scale:!1,category:"objects"},camera_flash:{keywords:["photography","gadgets"],char:"\u{1f4f8}",fitzpatrick_scale:!1,category:"objects"},video_camera:{keywords:["film","record"],char:"\u{1f4f9}",fitzpatrick_scale:!1,category:"objects"},movie_camera:{keywords:["film","record"],char:"\u{1f3a5}",fitzpatrick_scale:!1,category:"objects"},film_projector:{keywords:["video","tape","record","movie"],char:"\u{1f4fd}",fitzpatrick_scale:!1,category:"objects"},film_strip:{keywords:["movie"],char:"\u{1f39e}",fitzpatrick_scale:!1,category:"objects"},telephone_receiver:{keywords:["technology","communication","dial"],char:"\u{1f4de}",fitzpatrick_scale:!1,category:"objects"},phone:{keywords:["technology","communication","dial","telephone"],char:"\u260e\ufe0f",fitzpatrick_scale:!1,category:"objects"},pager:{keywords:["bbcall","oldschool","90s"],char:"\u{1f4df}",fitzpatrick_scale:!1,category:"objects"},fax:{keywords:["communication","technology"],char:"\u{1f4e0}",fitzpatrick_scale:!1,category:"objects"},tv:{keywords:["technology","program","oldschool","show","television"],char:"\u{1f4fa}",fitzpatrick_scale:!1,category:"objects"},radio:{keywords:["communication","music","podcast","program"],char:"\u{1f4fb}",fitzpatrick_scale:!1,category:"objects"},studio_microphone:{keywords:["sing","recording","artist","talkshow"],char:"\u{1f399}",fitzpatrick_scale:!1,category:"objects"},level_slider:{keywords:["scale"],char:"\u{1f39a}",fitzpatrick_scale:!1,category:"objects"},control_knobs:{keywords:["dial"],char:"\u{1f39b}",fitzpatrick_scale:!1,category:"objects"},compass:{keywords:["magnetic","navigation","orienteering"],char:"\u{1f9ed}",fitzpatrick_scale:!1,category:"objects"},stopwatch:{keywords:["time","deadline"],char:"\u23f1",fitzpatrick_scale:!1,category:"objects"},timer_clock:{keywords:["alarm"],char:"\u23f2",fitzpatrick_scale:!1,category:"objects"},alarm_clock:{keywords:["time","wake"],char:"\u23f0",fitzpatrick_scale:!1,category:"objects"},mantelpiece_clock:{keywords:["time"],char:"\u{1f570}",fitzpatrick_scale:!1,category:"objects"},hourglass_flowing_sand:{keywords:["oldschool","time","countdown"],char:"\u23f3",fitzpatrick_scale:!1,category:"objects"},hourglass:{keywords:["time","clock","oldschool","limit","exam","quiz","test"],char:"\u231b",fitzpatrick_scale:!1,category:"objects"},satellite:{keywords:["communication","future","radio","space"],char:"\u{1f4e1}",fitzpatrick_scale:!1,category:"objects"},battery:{keywords:["power","energy","sustain"],char:"\u{1f50b}",fitzpatrick_scale:!1,category:"objects"},electric_plug:{keywords:["charger","power"],char:"\u{1f50c}",fitzpatrick_scale:!1,category:"objects"},bulb:{keywords:["light","electricity","idea"],char:"\u{1f4a1}",fitzpatrick_scale:!1,category:"objects"},flashlight:{keywords:["dark","camping","sight","night"],char:"\u{1f526}",fitzpatrick_scale:!1,category:"objects"},candle:{keywords:["fire","wax"],char:"\u{1f56f}",fitzpatrick_scale:!1,category:"objects"},fire_extinguisher:{keywords:["quench"],char:"\u{1f9ef}",fitzpatrick_scale:!1,category:"objects"},wastebasket:{keywords:["bin","trash","rubbish","garbage","toss"],char:"\u{1f5d1}",fitzpatrick_scale:!1,category:"objects"},oil_drum:{keywords:["barrell"],char:"\u{1f6e2}",fitzpatrick_scale:!1,category:"objects"},money_with_wings:{keywords:["dollar","bills","payment","sale"],char:"\u{1f4b8}",fitzpatrick_scale:!1,category:"objects"},dollar:{keywords:["money","sales","bill","currency"],char:"\u{1f4b5}",fitzpatrick_scale:!1,category:"objects"},yen:{keywords:["money","sales","japanese","dollar","currency"],char:"\u{1f4b4}",fitzpatrick_scale:!1,category:"objects"},euro:{keywords:["money","sales","dollar","currency"],char:"\u{1f4b6}",fitzpatrick_scale:!1,category:"objects"},pound:{keywords:["british","sterling","money","sales","bills","uk","england","currency"],char:"\u{1f4b7}",fitzpatrick_scale:!1,category:"objects"},moneybag:{keywords:["dollar","payment","coins","sale"],char:"\u{1f4b0}",fitzpatrick_scale:!1,category:"objects"},credit_card:{keywords:["money","sales","dollar","bill","payment","shopping"],char:"\u{1f4b3}",fitzpatrick_scale:!1,category:"objects"},gem:{keywords:["blue","ruby","diamond","jewelry"],char:"\u{1f48e}",fitzpatrick_scale:!1,category:"objects"},balance_scale:{keywords:["law","fairness","weight"],char:"\u2696",fitzpatrick_scale:!1,category:"objects"},toolbox:{keywords:["tools","diy","fix","maintainer","mechanic"],char:"\u{1f9f0}",fitzpatrick_scale:!1,category:"objects"},wrench:{keywords:["tools","diy","ikea","fix","maintainer"],char:"\u{1f527}",fitzpatrick_scale:!1,category:"objects"},hammer:{keywords:["tools","build","create"],char:"\u{1f528}",fitzpatrick_scale:!1,category:"objects"},hammer_and_pick:{keywords:["tools","build","create"],char:"\u2692",fitzpatrick_scale:!1,category:"objects"},hammer_and_wrench:{keywords:["tools","build","create"],char:"\u{1f6e0}",fitzpatrick_scale:!1,category:"objects"},pick:{keywords:["tools","dig"],char:"\u26cf",fitzpatrick_scale:!1,category:"objects"},nut_and_bolt:{keywords:["handy","tools","fix"],char:"\u{1f529}",fitzpatrick_scale:!1,category:"objects"},gear:{keywords:["cog"],char:"\u2699",fitzpatrick_scale:!1,category:"objects"},brick:{keywords:["bricks"],char:"\u{1f9f1}",fitzpatrick_scale:!1,category:"objects"},chains:{keywords:["lock","arrest"],char:"\u26d3",fitzpatrick_scale:!1,category:"objects"},magnet:{keywords:["attraction","magnetic"],char:"\u{1f9f2}",fitzpatrick_scale:!1,category:"objects"},gun:{keywords:["violence","weapon","pistol","revolver"],char:"\u{1f52b}",fitzpatrick_scale:!1,category:"objects"},bomb:{keywords:["boom","explode","explosion","terrorism"],char:"\u{1f4a3}",fitzpatrick_scale:!1,category:"objects"},firecracker:{keywords:["dynamite","boom","explode","explosion","explosive"],char:"\u{1f9e8}",fitzpatrick_scale:!1,category:"objects"},hocho:{keywords:["knife","blade","cutlery","kitchen","weapon"],char:"\u{1f52a}",fitzpatrick_scale:!1,category:"objects"},dagger:{keywords:["weapon"],char:"\u{1f5e1}",fitzpatrick_scale:!1,category:"objects"},crossed_swords:{keywords:["weapon"],char:"\u2694",fitzpatrick_scale:!1,category:"objects"},shield:{keywords:["protection","security"],char:"\u{1f6e1}",fitzpatrick_scale:!1,category:"objects"},smoking:{keywords:["kills","tobacco","cigarette","joint","smoke"],char:"\u{1f6ac}",fitzpatrick_scale:!1,category:"objects"},skull_and_crossbones:{keywords:["poison","danger","deadly","scary","death","pirate","evil"],char:"\u2620",fitzpatrick_scale:!1,category:"objects"},coffin:{keywords:["vampire","dead","die","death","rip","graveyard","cemetery","casket","funeral","box"],char:"\u26b0",fitzpatrick_scale:!1,category:"objects"},funeral_urn:{keywords:["dead","die","death","rip","ashes"],char:"\u26b1",fitzpatrick_scale:!1,category:"objects"},amphora:{keywords:["vase","jar"],char:"\u{1f3fa}",fitzpatrick_scale:!1,category:"objects"},crystal_ball:{keywords:["disco","party","magic","circus","fortune_teller"],char:"\u{1f52e}",fitzpatrick_scale:!1,category:"objects"},prayer_beads:{keywords:["dhikr","religious"],char:"\u{1f4ff}",fitzpatrick_scale:!1,category:"objects"},nazar_amulet:{keywords:["bead","charm"],char:"\u{1f9ff}",fitzpatrick_scale:!1,category:"objects"},barber:{keywords:["hair","salon","style"],char:"\u{1f488}",fitzpatrick_scale:!1,category:"objects"},alembic:{keywords:["distilling","science","experiment","chemistry"],char:"\u2697",fitzpatrick_scale:!1,category:"objects"},telescope:{keywords:["stars","space","zoom","science","astronomy"],char:"\u{1f52d}",fitzpatrick_scale:!1,category:"objects"},microscope:{keywords:["laboratory","experiment","zoomin","science","study"],char:"\u{1f52c}",fitzpatrick_scale:!1,category:"objects"},hole:{keywords:["embarrassing"],char:"\u{1f573}",fitzpatrick_scale:!1,category:"objects"},pill:{keywords:["health","medicine","doctor","pharmacy","drug"],char:"\u{1f48a}",fitzpatrick_scale:!1,category:"objects"},syringe:{keywords:["health","hospital","drugs","blood","medicine","needle","doctor","nurse"],char:"\u{1f489}",fitzpatrick_scale:!1,category:"objects"},dna:{keywords:["biologist","genetics","life"],char:"\u{1f9ec}",fitzpatrick_scale:!1,category:"objects"},microbe:{keywords:["amoeba","bacteria","germs"],char:"\u{1f9a0}",fitzpatrick_scale:!1,category:"objects"},petri_dish:{keywords:["bacteria","biology","culture","lab"],char:"\u{1f9eb}",fitzpatrick_scale:!1,category:"objects"},test_tube:{keywords:["chemistry","experiment","lab","science"],char:"\u{1f9ea}",fitzpatrick_scale:!1,category:"objects"},thermometer:{keywords:["weather","temperature","hot","cold"],char:"\u{1f321}",fitzpatrick_scale:!1,category:"objects"},broom:{keywords:["cleaning","sweeping","witch"],char:"\u{1f9f9}",fitzpatrick_scale:!1,category:"objects"},basket:{keywords:["laundry"],char:"\u{1f9fa}",fitzpatrick_scale:!1,category:"objects"},toilet_paper:{keywords:["roll"],char:"\u{1f9fb}",fitzpatrick_scale:!1,category:"objects"},label:{keywords:["sale","tag"],char:"\u{1f3f7}",fitzpatrick_scale:!1,category:"objects"},bookmark:{keywords:["favorite","label","save"],char:"\u{1f516}",fitzpatrick_scale:!1,category:"objects"},toilet:{keywords:["restroom","wc","washroom","bathroom","potty"],char:"\u{1f6bd}",fitzpatrick_scale:!1,category:"objects"},shower:{keywords:["clean","water","bathroom"],char:"\u{1f6bf}",fitzpatrick_scale:!1,category:"objects"},bathtub:{keywords:["clean","shower","bathroom"],char:"\u{1f6c1}",fitzpatrick_scale:!1,category:"objects"},soap:{keywords:["bar","bathing","cleaning","lather"],char:"\u{1f9fc}",fitzpatrick_scale:!1,category:"objects"},sponge:{keywords:["absorbing","cleaning","porous"],char:"\u{1f9fd}",fitzpatrick_scale:!1,category:"objects"},lotion_bottle:{keywords:["moisturizer","sunscreen"],char:"\u{1f9f4}",fitzpatrick_scale:!1,category:"objects"},key:{keywords:["lock","door","password"],char:"\u{1f511}",fitzpatrick_scale:!1,category:"objects"},old_key:{keywords:["lock","door","password"],char:"\u{1f5dd}",fitzpatrick_scale:!1,category:"objects"},couch_and_lamp:{keywords:["read","chill"],char:"\u{1f6cb}",fitzpatrick_scale:!1,category:"objects"},sleeping_bed:{keywords:["bed","rest"],char:"\u{1f6cc}",fitzpatrick_scale:!0,category:"objects"},bed:{keywords:["sleep","rest"],char:"\u{1f6cf}",fitzpatrick_scale:!1,category:"objects"},door:{keywords:["house","entry","exit"],char:"\u{1f6aa}",fitzpatrick_scale:!1,category:"objects"},bellhop_bell:{keywords:["service"],char:"\u{1f6ce}",fitzpatrick_scale:!1,category:"objects"},teddy_bear:{keywords:["plush","stuffed"],char:"\u{1f9f8}",fitzpatrick_scale:!1,category:"objects"},framed_picture:{keywords:["photography"],char:"\u{1f5bc}",fitzpatrick_scale:!1,category:"objects"},world_map:{keywords:["location","direction"],char:"\u{1f5fa}",fitzpatrick_scale:!1,category:"objects"},parasol_on_ground:{keywords:["weather","summer"],char:"\u26f1",fitzpatrick_scale:!1,category:"objects"},moyai:{keywords:["rock","easter island","moai"],char:"\u{1f5ff}",fitzpatrick_scale:!1,category:"objects"},shopping:{keywords:["mall","buy","purchase"],char:"\u{1f6cd}",fitzpatrick_scale:!1,category:"objects"},shopping_cart:{keywords:["trolley"],char:"\u{1f6d2}",fitzpatrick_scale:!1,category:"objects"},balloon:{keywords:["party","celebration","birthday","circus"],char:"\u{1f388}",fitzpatrick_scale:!1,category:"objects"},flags:{keywords:["fish","japanese","koinobori","carp","banner"],char:"\u{1f38f}",fitzpatrick_scale:!1,category:"objects"},ribbon:{keywords:["decoration","pink","girl","bowtie"],char:"\u{1f380}",fitzpatrick_scale:!1,category:"objects"},gift:{keywords:["present","birthday","christmas","xmas"],char:"\u{1f381}",fitzpatrick_scale:!1,category:"objects"},confetti_ball:{keywords:["festival","party","birthday","circus"],char:"\u{1f38a}",fitzpatrick_scale:!1,category:"objects"},tada:{keywords:["party","congratulations","birthday","magic","circus","celebration"],char:"\u{1f389}",fitzpatrick_scale:!1,category:"objects"},dolls:{keywords:["japanese","toy","kimono"],char:"\u{1f38e}",fitzpatrick_scale:!1,category:"objects"},wind_chime:{keywords:["nature","ding","spring","bell"],char:"\u{1f390}",fitzpatrick_scale:!1,category:"objects"},crossed_flags:{keywords:["japanese","nation","country","border"],char:"\u{1f38c}",fitzpatrick_scale:!1,category:"objects"},izakaya_lantern:{keywords:["light","paper","halloween","spooky"],char:"\u{1f3ee}",fitzpatrick_scale:!1,category:"objects"},red_envelope:{keywords:["gift"],char:"\u{1f9e7}",fitzpatrick_scale:!1,category:"objects"},email:{keywords:["letter","postal","inbox","communication"],char:"\u2709\ufe0f",fitzpatrick_scale:!1,category:"objects"},envelope_with_arrow:{keywords:["email","communication"],char:"\u{1f4e9}",fitzpatrick_scale:!1,category:"objects"},incoming_envelope:{keywords:["email","inbox"],char:"\u{1f4e8}",fitzpatrick_scale:!1,category:"objects"},"e-mail":{keywords:["communication","inbox"],char:"\u{1f4e7}",fitzpatrick_scale:!1,category:"objects"},love_letter:{keywords:["email","like","affection","envelope","valentines"],char:"\u{1f48c}",fitzpatrick_scale:!1,category:"objects"},postbox:{keywords:["email","letter","envelope"],char:"\u{1f4ee}",fitzpatrick_scale:!1,category:"objects"},mailbox_closed:{keywords:["email","communication","inbox"],char:"\u{1f4ea}",fitzpatrick_scale:!1,category:"objects"},mailbox:{keywords:["email","inbox","communication"],char:"\u{1f4eb}",fitzpatrick_scale:!1,category:"objects"},mailbox_with_mail:{keywords:["email","inbox","communication"],char:"\u{1f4ec}",fitzpatrick_scale:!1,category:"objects"},mailbox_with_no_mail:{keywords:["email","inbox"],char:"\u{1f4ed}",fitzpatrick_scale:!1,category:"objects"},package:{keywords:["mail","gift","cardboard","box","moving"],char:"\u{1f4e6}",fitzpatrick_scale:!1,category:"objects"},postal_horn:{keywords:["instrument","music"],char:"\u{1f4ef}",fitzpatrick_scale:!1,category:"objects"},inbox_tray:{keywords:["email","documents"],char:"\u{1f4e5}",fitzpatrick_scale:!1,category:"objects"},outbox_tray:{keywords:["inbox","email"],char:"\u{1f4e4}",fitzpatrick_scale:!1,category:"objects"},scroll:{keywords:["documents","ancient","history","paper"],char:"\u{1f4dc}",fitzpatrick_scale:!1,category:"objects"},page_with_curl:{keywords:["documents","office","paper"],char:"\u{1f4c3}",fitzpatrick_scale:!1,category:"objects"},bookmark_tabs:{keywords:["favorite","save","order","tidy"],char:"\u{1f4d1}",fitzpatrick_scale:!1,category:"objects"},receipt:{keywords:["accounting","expenses"],char:"\u{1f9fe}",fitzpatrick_scale:!1,category:"objects"},bar_chart:{keywords:["graph","presentation","stats"],char:"\u{1f4ca}",fitzpatrick_scale:!1,category:"objects"},chart_with_upwards_trend:{keywords:["graph","presentation","stats","recovery","business","economics","money","sales","good","success"],char:"\u{1f4c8}",fitzpatrick_scale:!1,category:"objects"},chart_with_downwards_trend:{keywords:["graph","presentation","stats","recession","business","economics","money","sales","bad","failure"],char:"\u{1f4c9}",fitzpatrick_scale:!1,category:"objects"},page_facing_up:{keywords:["documents","office","paper","information"],char:"\u{1f4c4}",fitzpatrick_scale:!1,category:"objects"},date:{keywords:["calendar","schedule"],char:"\u{1f4c5}",fitzpatrick_scale:!1,category:"objects"},calendar:{keywords:["schedule","date","planning"],char:"\u{1f4c6}",fitzpatrick_scale:!1,category:"objects"},spiral_calendar:{keywords:["date","schedule","planning"],char:"\u{1f5d3}",fitzpatrick_scale:!1,category:"objects"},card_index:{keywords:["business","stationery"],char:"\u{1f4c7}",fitzpatrick_scale:!1,category:"objects"},card_file_box:{keywords:["business","stationery"],char:"\u{1f5c3}",fitzpatrick_scale:!1,category:"objects"},ballot_box:{keywords:["election","vote"],char:"\u{1f5f3}",fitzpatrick_scale:!1,category:"objects"},file_cabinet:{keywords:["filing","organizing"],char:"\u{1f5c4}",fitzpatrick_scale:!1,category:"objects"},clipboard:{keywords:["stationery","documents"],char:"\u{1f4cb}",fitzpatrick_scale:!1,category:"objects"},spiral_notepad:{keywords:["memo","stationery"],char:"\u{1f5d2}",fitzpatrick_scale:!1,category:"objects"},file_folder:{keywords:["documents","business","office"],char:"\u{1f4c1}",fitzpatrick_scale:!1,category:"objects"},open_file_folder:{keywords:["documents","load"],char:"\u{1f4c2}",fitzpatrick_scale:!1,category:"objects"},card_index_dividers:{keywords:["organizing","business","stationery"],char:"\u{1f5c2}",fitzpatrick_scale:!1,category:"objects"},newspaper_roll:{keywords:["press","headline"],char:"\u{1f5de}",fitzpatrick_scale:!1,category:"objects"},newspaper:{keywords:["press","headline"],char:"\u{1f4f0}",fitzpatrick_scale:!1,category:"objects"},notebook:{keywords:["stationery","record","notes","paper","study"],char:"\u{1f4d3}",fitzpatrick_scale:!1,category:"objects"},closed_book:{keywords:["read","library","knowledge","textbook","learn"],char:"\u{1f4d5}",fitzpatrick_scale:!1,category:"objects"},green_book:{keywords:["read","library","knowledge","study"],char:"\u{1f4d7}",fitzpatrick_scale:!1,category:"objects"},blue_book:{keywords:["read","library","knowledge","learn","study"],char:"\u{1f4d8}",fitzpatrick_scale:!1,category:"objects"},orange_book:{keywords:["read","library","knowledge","textbook","study"],char:"\u{1f4d9}",fitzpatrick_scale:!1,category:"objects"},notebook_with_decorative_cover:{keywords:["classroom","notes","record","paper","study"],char:"\u{1f4d4}",fitzpatrick_scale:!1,category:"objects"},ledger:{keywords:["notes","paper"],char:"\u{1f4d2}",fitzpatrick_scale:!1,category:"objects"},books:{keywords:["literature","library","study"],char:"\u{1f4da}",fitzpatrick_scale:!1,category:"objects"},open_book:{keywords:["book","read","library","knowledge","literature","learn","study"],char:"\u{1f4d6}",fitzpatrick_scale:!1,category:"objects"},safety_pin:{keywords:["diaper"],char:"\u{1f9f7}",fitzpatrick_scale:!1,category:"objects"},link:{keywords:["rings","url"],char:"\u{1f517}",fitzpatrick_scale:!1,category:"objects"},paperclip:{keywords:["documents","stationery"],char:"\u{1f4ce}",fitzpatrick_scale:!1,category:"objects"},paperclips:{keywords:["documents","stationery"],char:"\u{1f587}",fitzpatrick_scale:!1,category:"objects"},scissors:{keywords:["stationery","cut"],char:"\u2702\ufe0f",fitzpatrick_scale:!1,category:"objects"},triangular_ruler:{keywords:["stationery","math","architect","sketch"],char:"\u{1f4d0}",fitzpatrick_scale:!1,category:"objects"},straight_ruler:{keywords:["stationery","calculate","length","math","school","drawing","architect","sketch"],char:"\u{1f4cf}",fitzpatrick_scale:!1,category:"objects"},abacus:{keywords:["calculation"],char:"\u{1f9ee}",fitzpatrick_scale:!1,category:"objects"},pushpin:{keywords:["stationery","mark","here"],char:"\u{1f4cc}",fitzpatrick_scale:!1,category:"objects"},round_pushpin:{keywords:["stationery","location","map","here"],char:"\u{1f4cd}",fitzpatrick_scale:!1,category:"objects"},triangular_flag_on_post:{keywords:["mark","milestone","place"],char:"\u{1f6a9}",fitzpatrick_scale:!1,category:"objects"},white_flag:{keywords:["losing","loser","lost","surrender","give up","fail"],char:"\u{1f3f3}",fitzpatrick_scale:!1,category:"objects"},black_flag:{keywords:["pirate"],char:"\u{1f3f4}",fitzpatrick_scale:!1,category:"objects"},rainbow_flag:{keywords:["flag","rainbow","pride","gay","lgbt","glbt","queer","homosexual","lesbian","bisexual","transgender"],char:"\u{1f3f3}\ufe0f\u200d\u{1f308}",fitzpatrick_scale:!1,category:"objects"},closed_lock_with_key:{keywords:["security","privacy"],char:"\u{1f510}",fitzpatrick_scale:!1,category:"objects"},lock:{keywords:["security","password","padlock"],char:"\u{1f512}",fitzpatrick_scale:!1,category:"objects"},unlock:{keywords:["privacy","security"],char:"\u{1f513}",fitzpatrick_scale:!1,category:"objects"},lock_with_ink_pen:{keywords:["security","secret"],char:"\u{1f50f}",fitzpatrick_scale:!1,category:"objects"},pen:{keywords:["stationery","writing","write"],char:"\u{1f58a}",fitzpatrick_scale:!1,category:"objects"},fountain_pen:{keywords:["stationery","writing","write"],char:"\u{1f58b}",fitzpatrick_scale:!1,category:"objects"},black_nib:{keywords:["pen","stationery","writing","write"],char:"\u2712\ufe0f",fitzpatrick_scale:!1,category:"objects"},memo:{keywords:["write","documents","stationery","pencil","paper","writing","legal","exam","quiz","test","study","compose"],char:"\u{1f4dd}",fitzpatrick_scale:!1,category:"objects"},pencil2:{keywords:["stationery","write","paper","writing","school","study"],char:"\u270f\ufe0f",fitzpatrick_scale:!1,category:"objects"},crayon:{keywords:["drawing","creativity"],char:"\u{1f58d}",fitzpatrick_scale:!1,category:"objects"},paintbrush:{keywords:["drawing","creativity","art"],char:"\u{1f58c}",fitzpatrick_scale:!1,category:"objects"},mag:{keywords:["search","zoom","find","detective"],char:"\u{1f50d}",fitzpatrick_scale:!1,category:"objects"},mag_right:{keywords:["search","zoom","find","detective"],char:"\u{1f50e}",fitzpatrick_scale:!1,category:"objects"},heart:{keywords:["love","like","valentines"],char:"\u2764\ufe0f",fitzpatrick_scale:!1,category:"symbols"},orange_heart:{keywords:["love","like","affection","valentines"],char:"\u{1f9e1}",fitzpatrick_scale:!1,category:"symbols"},yellow_heart:{keywords:["love","like","affection","valentines"],char:"\u{1f49b}",fitzpatrick_scale:!1,category:"symbols"},green_heart:{keywords:["love","like","affection","valentines"],char:"\u{1f49a}",fitzpatrick_scale:!1,category:"symbols"},blue_heart:{keywords:["love","like","affection","valentines"],char:"\u{1f499}",fitzpatrick_scale:!1,category:"symbols"},purple_heart:{keywords:["love","like","affection","valentines"],char:"\u{1f49c}",fitzpatrick_scale:!1,category:"symbols"},black_heart:{keywords:["evil"],char:"\u{1f5a4}",fitzpatrick_scale:!1,category:"symbols"},broken_heart:{keywords:["sad","sorry","break","heart","heartbreak"],char:"\u{1f494}",fitzpatrick_scale:!1,category:"symbols"},heavy_heart_exclamation:{keywords:["decoration","love"],char:"\u2763",fitzpatrick_scale:!1,category:"symbols"},two_hearts:{keywords:["love","like","affection","valentines","heart"],char:"\u{1f495}",fitzpatrick_scale:!1,category:"symbols"},revolving_hearts:{keywords:["love","like","affection","valentines"],char:"\u{1f49e}",fitzpatrick_scale:!1,category:"symbols"},heartbeat:{keywords:["love","like","affection","valentines","pink","heart"],char:"\u{1f493}",fitzpatrick_scale:!1,category:"symbols"},heartpulse:{keywords:["like","love","affection","valentines","pink"],char:"\u{1f497}",fitzpatrick_scale:!1,category:"symbols"},sparkling_heart:{keywords:["love","like","affection","valentines"],char:"\u{1f496}",fitzpatrick_scale:!1,category:"symbols"},cupid:{keywords:["love","like","heart","affection","valentines"],char:"\u{1f498}",fitzpatrick_scale:!1,category:"symbols"},gift_heart:{keywords:["love","valentines"],char:"\u{1f49d}",fitzpatrick_scale:!1,category:"symbols"},heart_decoration:{keywords:["purple-square","love","like"],char:"\u{1f49f}",fitzpatrick_scale:!1,category:"symbols"},peace_symbol:{keywords:["hippie"],char:"\u262e",fitzpatrick_scale:!1,category:"symbols"},latin_cross:{keywords:["christianity"],char:"\u271d",fitzpatrick_scale:!1,category:"symbols"},star_and_crescent:{keywords:["islam"],char:"\u262a",fitzpatrick_scale:!1,category:"symbols"},om:{keywords:["hinduism","buddhism","sikhism","jainism"],char:"\u{1f549}",fitzpatrick_scale:!1,category:"symbols"},wheel_of_dharma:{keywords:["hinduism","buddhism","sikhism","jainism"],char:"\u2638",fitzpatrick_scale:!1,category:"symbols"},star_of_david:{keywords:["judaism"],char:"\u2721",fitzpatrick_scale:!1,category:"symbols"},six_pointed_star:{keywords:["purple-square","religion","jewish","hexagram"],char:"\u{1f52f}",fitzpatrick_scale:!1,category:"symbols"},menorah:{keywords:["hanukkah","candles","jewish"],char:"\u{1f54e}",fitzpatrick_scale:!1,category:"symbols"},yin_yang:{keywords:["balance"],char:"\u262f",fitzpatrick_scale:!1,category:"symbols"},orthodox_cross:{keywords:["suppedaneum","religion"],char:"\u2626",fitzpatrick_scale:!1,category:"symbols"},place_of_worship:{keywords:["religion","church","temple","prayer"],char:"\u{1f6d0}",fitzpatrick_scale:!1,category:"symbols"},ophiuchus:{keywords:["sign","purple-square","constellation","astrology"],char:"\u26ce",fitzpatrick_scale:!1,category:"symbols"},aries:{keywords:["sign","purple-square","zodiac","astrology"],char:"\u2648",fitzpatrick_scale:!1,category:"symbols"},taurus:{keywords:["purple-square","sign","zodiac","astrology"],char:"\u2649",fitzpatrick_scale:!1,category:"symbols"},gemini:{keywords:["sign","zodiac","purple-square","astrology"],char:"\u264a",fitzpatrick_scale:!1,category:"symbols"},cancer:{keywords:["sign","zodiac","purple-square","astrology"],char:"\u264b",fitzpatrick_scale:!1,category:"symbols"},leo:{keywords:["sign","purple-square","zodiac","astrology"],char:"\u264c",fitzpatrick_scale:!1,category:"symbols"},virgo:{keywords:["sign","zodiac","purple-square","astrology"],char:"\u264d",fitzpatrick_scale:!1,category:"symbols"},libra:{keywords:["sign","purple-square","zodiac","astrology"],char:"\u264e",fitzpatrick_scale:!1,category:"symbols"},scorpius:{keywords:["sign","zodiac","purple-square","astrology","scorpio"],char:"\u264f",fitzpatrick_scale:!1,category:"symbols"},sagittarius:{keywords:["sign","zodiac","purple-square","astrology"],char:"\u2650",fitzpatrick_scale:!1,category:"symbols"},capricorn:{keywords:["sign","zodiac","purple-square","astrology"],char:"\u2651",fitzpatrick_scale:!1,category:"symbols"},aquarius:{keywords:["sign","purple-square","zodiac","astrology"],char:"\u2652",fitzpatrick_scale:!1,category:"symbols"},pisces:{keywords:["purple-square","sign","zodiac","astrology"],char:"\u2653",fitzpatrick_scale:!1,category:"symbols"},id:{keywords:["purple-square","words"],char:"\u{1f194}",fitzpatrick_scale:!1,category:"symbols"},atom_symbol:{keywords:["science","physics","chemistry"],char:"\u269b",fitzpatrick_scale:!1,category:"symbols"},u7a7a:{keywords:["kanji","japanese","chinese","empty","sky","blue-square"],char:"\u{1f233}",fitzpatrick_scale:!1,category:"symbols"},u5272:{keywords:["cut","divide","chinese","kanji","pink-square"],char:"\u{1f239}",fitzpatrick_scale:!1,category:"symbols"},radioactive:{keywords:["nuclear","danger"],char:"\u2622",fitzpatrick_scale:!1,category:"symbols"},biohazard:{keywords:["danger"],char:"\u2623",fitzpatrick_scale:!1,category:"symbols"},mobile_phone_off:{keywords:["mute","orange-square","silence","quiet"],char:"\u{1f4f4}",fitzpatrick_scale:!1,category:"symbols"},vibration_mode:{keywords:["orange-square","phone"],char:"\u{1f4f3}",fitzpatrick_scale:!1,category:"symbols"},u6709:{keywords:["orange-square","chinese","have","kanji"],char:"\u{1f236}",fitzpatrick_scale:!1,category:"symbols"},u7121:{keywords:["nothing","chinese","kanji","japanese","orange-square"],char:"\u{1f21a}",fitzpatrick_scale:!1,category:"symbols"},u7533:{keywords:["chinese","japanese","kanji","orange-square"],char:"\u{1f238}",fitzpatrick_scale:!1,category:"symbols"},u55b6:{keywords:["japanese","opening hours","orange-square"],char:"\u{1f23a}",fitzpatrick_scale:!1,category:"symbols"},u6708:{keywords:["chinese","month","moon","japanese","orange-square","kanji"],char:"\u{1f237}\ufe0f",fitzpatrick_scale:!1,category:"symbols"},eight_pointed_black_star:{keywords:["orange-square","shape","polygon"],char:"\u2734\ufe0f",fitzpatrick_scale:!1,category:"symbols"},vs:{keywords:["words","orange-square"],char:"\u{1f19a}",fitzpatrick_scale:!1,category:"symbols"},accept:{keywords:["ok","good","chinese","kanji","agree","yes","orange-circle"],char:"\u{1f251}",fitzpatrick_scale:!1,category:"symbols"},white_flower:{keywords:["japanese","spring"],char:"\u{1f4ae}",fitzpatrick_scale:!1,category:"symbols"},ideograph_advantage:{keywords:["chinese","kanji","obtain","get","circle"],char:"\u{1f250}",fitzpatrick_scale:!1,category:"symbols"},secret:{keywords:["privacy","chinese","sshh","kanji","red-circle"],char:"\u3299\ufe0f",fitzpatrick_scale:!1,category:"symbols"},congratulations:{keywords:["chinese","kanji","japanese","red-circle"],char:"\u3297\ufe0f",fitzpatrick_scale:!1,category:"symbols"},u5408:{keywords:["japanese","chinese","join","kanji","red-square"],char:"\u{1f234}",fitzpatrick_scale:!1,category:"symbols"},u6e80:{keywords:["full","chinese","japanese","red-square","kanji"],char:"\u{1f235}",fitzpatrick_scale:!1,category:"symbols"},u7981:{keywords:["kanji","japanese","chinese","forbidden","limit","restricted","red-square"],char:"\u{1f232}",fitzpatrick_scale:!1,category:"symbols"},a:{keywords:["red-square","alphabet","letter"],char:"\u{1f170}\ufe0f",fitzpatrick_scale:!1,category:"symbols"},b:{keywords:["red-square","alphabet","letter"],char:"\u{1f171}\ufe0f",fitzpatrick_scale:!1,category:"symbols"},ab:{keywords:["red-square","alphabet"],char:"\u{1f18e}",fitzpatrick_scale:!1,category:"symbols"},cl:{keywords:["alphabet","words","red-square"],char:"\u{1f191}",fitzpatrick_scale:!1,category:"symbols"},o2:{keywords:["alphabet","red-square","letter"],char:"\u{1f17e}\ufe0f",fitzpatrick_scale:!1,category:"symbols"},sos:{keywords:["help","red-square","words","emergency","911"],char:"\u{1f198}",fitzpatrick_scale:!1,category:"symbols"},no_entry:{keywords:["limit","security","privacy","bad","denied","stop","circle"],char:"\u26d4",fitzpatrick_scale:!1,category:"symbols"},name_badge:{keywords:["fire","forbid"],char:"\u{1f4db}",fitzpatrick_scale:!1,category:"symbols"},no_entry_sign:{keywords:["forbid","stop","limit","denied","disallow","circle"],char:"\u{1f6ab}",fitzpatrick_scale:!1,category:"symbols"},x:{keywords:["no","delete","remove","cancel","red"],char:"\u274c",fitzpatrick_scale:!1,category:"symbols"},o:{keywords:["circle","round"],char:"\u2b55",fitzpatrick_scale:!1,category:"symbols"},stop_sign:{keywords:["stop"],char:"\u{1f6d1}",fitzpatrick_scale:!1,category:"symbols"},anger:{keywords:["angry","mad"],char:"\u{1f4a2}",fitzpatrick_scale:!1,category:"symbols"},hotsprings:{keywords:["bath","warm","relax"],char:"\u2668\ufe0f",fitzpatrick_scale:!1,category:"symbols"},no_pedestrians:{keywords:["rules","crossing","walking","circle"],char:"\u{1f6b7}",fitzpatrick_scale:!1,category:"symbols"},do_not_litter:{keywords:["trash","bin","garbage","circle"],char:"\u{1f6af}",fitzpatrick_scale:!1,category:"symbols"},no_bicycles:{keywords:["cyclist","prohibited","circle"],char:"\u{1f6b3}",fitzpatrick_scale:!1,category:"symbols"},"non-potable_water":{keywords:["drink","faucet","tap","circle"],char:"\u{1f6b1}",fitzpatrick_scale:!1,category:"symbols"},underage:{keywords:["18","drink","pub","night","minor","circle"],char:"\u{1f51e}",fitzpatrick_scale:!1,category:"symbols"},no_mobile_phones:{keywords:["iphone","mute","circle"],char:"\u{1f4f5}",fitzpatrick_scale:!1,category:"symbols"},exclamation:{keywords:["heavy_exclamation_mark","danger","surprise","punctuation","wow","warning"],char:"\u2757",fitzpatrick_scale:!1,category:"symbols"},grey_exclamation:{keywords:["surprise","punctuation","gray","wow","warning"],char:"\u2755",fitzpatrick_scale:!1,category:"symbols"},question:{keywords:["doubt","confused"],char:"\u2753",fitzpatrick_scale:!1,category:"symbols"},grey_question:{keywords:["doubts","gray","huh","confused"],char:"\u2754",fitzpatrick_scale:!1,category:"symbols"},bangbang:{keywords:["exclamation","surprise"],char:"\u203c\ufe0f",fitzpatrick_scale:!1,category:"symbols"},interrobang:{keywords:["wat","punctuation","surprise"],char:"\u2049\ufe0f",fitzpatrick_scale:!1,category:"symbols"},100:{keywords:["score","perfect","numbers","century","exam","quiz","test","pass","hundred"],char:"\u{1f4af}",fitzpatrick_scale:!1,category:"symbols"},low_brightness:{keywords:["sun","afternoon","warm","summer"],char:"\u{1f505}",fitzpatrick_scale:!1,category:"symbols"},high_brightness:{keywords:["sun","light"],char:"\u{1f506}",fitzpatrick_scale:!1,category:"symbols"},trident:{keywords:["weapon","spear"],char:"\u{1f531}",fitzpatrick_scale:!1,category:"symbols"},fleur_de_lis:{keywords:["decorative","scout"],char:"\u269c",fitzpatrick_scale:!1,category:"symbols"},part_alternation_mark:{keywords:["graph","presentation","stats","business","economics","bad"],char:"\u303d\ufe0f",fitzpatrick_scale:!1,category:"symbols"},warning:{keywords:["exclamation","wip","alert","error","problem","issue"],char:"\u26a0\ufe0f",fitzpatrick_scale:!1,category:"symbols"},children_crossing:{keywords:["school","warning","danger","sign","driving","yellow-diamond"],char:"\u{1f6b8}",fitzpatrick_scale:!1,category:"symbols"},beginner:{keywords:["badge","shield"],char:"\u{1f530}",fitzpatrick_scale:!1,category:"symbols"},recycle:{keywords:["arrow","environment","garbage","trash"],char:"\u267b\ufe0f",fitzpatrick_scale:!1,category:"symbols"},u6307:{keywords:["chinese","point","green-square","kanji"],char:"\u{1f22f}",fitzpatrick_scale:!1,category:"symbols"},chart:{keywords:["green-square","graph","presentation","stats"],char:"\u{1f4b9}",fitzpatrick_scale:!1,category:"symbols"},sparkle:{keywords:["stars","green-square","awesome","good","fireworks"],char:"\u2747\ufe0f",fitzpatrick_scale:!1,category:"symbols"},eight_spoked_asterisk:{keywords:["star","sparkle","green-square"],char:"\u2733\ufe0f",fitzpatrick_scale:!1,category:"symbols"},negative_squared_cross_mark:{keywords:["x","green-square","no","deny"],char:"\u274e",fitzpatrick_scale:!1,category:"symbols"},white_check_mark:{keywords:["green-square","ok","agree","vote","election","answer","tick"],char:"\u2705",fitzpatrick_scale:!1,category:"symbols"},diamond_shape_with_a_dot_inside:{keywords:["jewel","blue","gem","crystal","fancy"],char:"\u{1f4a0}",fitzpatrick_scale:!1,category:"symbols"},cyclone:{keywords:["weather","swirl","blue","cloud","vortex","spiral","whirlpool","spin","tornado","hurricane","typhoon"],char:"\u{1f300}",fitzpatrick_scale:!1,category:"symbols"},loop:{keywords:["tape","cassette"],char:"\u27bf",fitzpatrick_scale:!1,category:"symbols"},globe_with_meridians:{keywords:["earth","international","world","internet","interweb","i18n"],char:"\u{1f310}",fitzpatrick_scale:!1,category:"symbols"},m:{keywords:["alphabet","blue-circle","letter"],char:"\u24c2\ufe0f",fitzpatrick_scale:!1,category:"symbols"},atm:{keywords:["money","sales","cash","blue-square","payment","bank"],char:"\u{1f3e7}",fitzpatrick_scale:!1,category:"symbols"},sa:{keywords:["japanese","blue-square","katakana"],char:"\u{1f202}\ufe0f",fitzpatrick_scale:!1,category:"symbols"},passport_control:{keywords:["custom","blue-square"],char:"\u{1f6c2}",fitzpatrick_scale:!1,category:"symbols"},customs:{keywords:["passport","border","blue-square"],char:"\u{1f6c3}",fitzpatrick_scale:!1,category:"symbols"},baggage_claim:{keywords:["blue-square","airport","transport"],char:"\u{1f6c4}",fitzpatrick_scale:!1,category:"symbols"},left_luggage:{keywords:["blue-square","travel"],char:"\u{1f6c5}",fitzpatrick_scale:!1,category:"symbols"},wheelchair:{keywords:["blue-square","disabled","a11y","accessibility"],char:"\u267f",fitzpatrick_scale:!1,category:"symbols"},no_smoking:{keywords:["cigarette","blue-square","smell","smoke"],char:"\u{1f6ad}",fitzpatrick_scale:!1,category:"symbols"},wc:{keywords:["toilet","restroom","blue-square"],char:"\u{1f6be}",fitzpatrick_scale:!1,category:"symbols"},parking:{keywords:["cars","blue-square","alphabet","letter"],char:"\u{1f17f}\ufe0f",fitzpatrick_scale:!1,category:"symbols"},potable_water:{keywords:["blue-square","liquid","restroom","cleaning","faucet"],char:"\u{1f6b0}",fitzpatrick_scale:!1,category:"symbols"},mens:{keywords:["toilet","restroom","wc","blue-square","gender","male"],char:"\u{1f6b9}",fitzpatrick_scale:!1,category:"symbols"},womens:{keywords:["purple-square","woman","female","toilet","loo","restroom","gender"],char:"\u{1f6ba}",fitzpatrick_scale:!1,category:"symbols"},baby_symbol:{keywords:["orange-square","child"],char:"\u{1f6bc}",fitzpatrick_scale:!1,category:"symbols"},restroom:{keywords:["blue-square","toilet","refresh","wc","gender"],char:"\u{1f6bb}",fitzpatrick_scale:!1,category:"symbols"},put_litter_in_its_place:{keywords:["blue-square","sign","human","info"],char:"\u{1f6ae}",fitzpatrick_scale:!1,category:"symbols"},cinema:{keywords:["blue-square","record","film","movie","curtain","stage","theater"],char:"\u{1f3a6}",fitzpatrick_scale:!1,category:"symbols"},signal_strength:{keywords:["blue-square","reception","phone","internet","connection","wifi","bluetooth","bars"],char:"\u{1f4f6}",fitzpatrick_scale:!1,category:"symbols"},koko:{keywords:["blue-square","here","katakana","japanese","destination"],char:"\u{1f201}",fitzpatrick_scale:!1,category:"symbols"},ng:{keywords:["blue-square","words","shape","icon"],char:"\u{1f196}",fitzpatrick_scale:!1,category:"symbols"},ok:{keywords:["good","agree","yes","blue-square"],char:"\u{1f197}",fitzpatrick_scale:!1,category:"symbols"},up:{keywords:["blue-square","above","high"],char:"\u{1f199}",fitzpatrick_scale:!1,category:"symbols"},cool:{keywords:["words","blue-square"],char:"\u{1f192}",fitzpatrick_scale:!1,category:"symbols"},new:{keywords:["blue-square","words","start"],char:"\u{1f195}",fitzpatrick_scale:!1,category:"symbols"},free:{keywords:["blue-square","words"],char:"\u{1f193}",fitzpatrick_scale:!1,category:"symbols"},zero:{keywords:["0","numbers","blue-square","null"],char:"0\ufe0f\u20e3",fitzpatrick_scale:!1,category:"symbols"},one:{keywords:["blue-square","numbers","1"],char:"1\ufe0f\u20e3",fitzpatrick_scale:!1,category:"symbols"},two:{keywords:["numbers","2","prime","blue-square"],char:"2\ufe0f\u20e3",fitzpatrick_scale:!1,category:"symbols"},three:{keywords:["3","numbers","prime","blue-square"],char:"3\ufe0f\u20e3",fitzpatrick_scale:!1,category:"symbols"},four:{keywords:["4","numbers","blue-square"],char:"4\ufe0f\u20e3",fitzpatrick_scale:!1,category:"symbols"},five:{keywords:["5","numbers","blue-square","prime"],char:"5\ufe0f\u20e3",fitzpatrick_scale:!1,category:"symbols"},six:{keywords:["6","numbers","blue-square"],char:"6\ufe0f\u20e3",fitzpatrick_scale:!1,category:"symbols"},seven:{keywords:["7","numbers","blue-square","prime"],char:"7\ufe0f\u20e3",fitzpatrick_scale:!1,category:"symbols"},eight:{keywords:["8","blue-square","numbers"],char:"8\ufe0f\u20e3",fitzpatrick_scale:!1,category:"symbols"},nine:{keywords:["blue-square","numbers","9"],char:"9\ufe0f\u20e3",fitzpatrick_scale:!1,category:"symbols"},keycap_ten:{keywords:["numbers","10","blue-square"],char:"\u{1f51f}",fitzpatrick_scale:!1,category:"symbols"},asterisk:{keywords:["star","keycap"],char:"*\u20e3",fitzpatrick_scale:!1,category:"symbols"},1234:{keywords:["numbers","blue-square"],char:"\u{1f522}",fitzpatrick_scale:!1,category:"symbols"},eject_button:{keywords:["blue-square"],char:"\u23cf\ufe0f",fitzpatrick_scale:!1,category:"symbols"},arrow_forward:{keywords:["blue-square","right","direction","play"],char:"\u25b6\ufe0f",fitzpatrick_scale:!1,category:"symbols"},pause_button:{keywords:["pause","blue-square"],char:"\u23f8",fitzpatrick_scale:!1,category:"symbols"},next_track_button:{keywords:["forward","next","blue-square"],char:"\u23ed",fitzpatrick_scale:!1,category:"symbols"},stop_button:{keywords:["blue-square"],char:"\u23f9",fitzpatrick_scale:!1,category:"symbols"},record_button:{keywords:["blue-square"],char:"\u23fa",fitzpatrick_scale:!1,category:"symbols"},play_or_pause_button:{keywords:["blue-square","play","pause"],char:"\u23ef",fitzpatrick_scale:!1,category:"symbols"},previous_track_button:{keywords:["backward"],char:"\u23ee",fitzpatrick_scale:!1,category:"symbols"},fast_forward:{keywords:["blue-square","play","speed","continue"],char:"\u23e9",fitzpatrick_scale:!1,category:"symbols"},rewind:{keywords:["play","blue-square"],char:"\u23ea",fitzpatrick_scale:!1,category:"symbols"},twisted_rightwards_arrows:{keywords:["blue-square","shuffle","music","random"],char:"\u{1f500}",fitzpatrick_scale:!1,category:"symbols"},repeat:{keywords:["loop","record"],char:"\u{1f501}",fitzpatrick_scale:!1,category:"symbols"},repeat_one:{keywords:["blue-square","loop"],char:"\u{1f502}",fitzpatrick_scale:!1,category:"symbols"},arrow_backward:{keywords:["blue-square","left","direction"],char:"\u25c0\ufe0f",fitzpatrick_scale:!1,category:"symbols"},arrow_up_small:{keywords:["blue-square","triangle","direction","point","forward","top"],char:"\u{1f53c}",fitzpatrick_scale:!1,category:"symbols"},arrow_down_small:{keywords:["blue-square","direction","bottom"],char:"\u{1f53d}",fitzpatrick_scale:!1,category:"symbols"},arrow_double_up:{keywords:["blue-square","direction","top"],char:"\u23eb",fitzpatrick_scale:!1,category:"symbols"},arrow_double_down:{keywords:["blue-square","direction","bottom"],char:"\u23ec",fitzpatrick_scale:!1,category:"symbols"},arrow_right:{keywords:["blue-square","next"],char:"\u27a1\ufe0f",fitzpatrick_scale:!1,category:"symbols"},arrow_left:{keywords:["blue-square","previous","back"],char:"\u2b05\ufe0f",fitzpatrick_scale:!1,category:"symbols"},arrow_up:{keywords:["blue-square","continue","top","direction"],char:"\u2b06\ufe0f",fitzpatrick_scale:!1,category:"symbols"},arrow_down:{keywords:["blue-square","direction","bottom"],char:"\u2b07\ufe0f",fitzpatrick_scale:!1,category:"symbols"},arrow_upper_right:{keywords:["blue-square","point","direction","diagonal","northeast"],char:"\u2197\ufe0f",fitzpatrick_scale:!1,category:"symbols"},arrow_lower_right:{keywords:["blue-square","direction","diagonal","southeast"],char:"\u2198\ufe0f",fitzpatrick_scale:!1,category:"symbols"},arrow_lower_left:{keywords:["blue-square","direction","diagonal","southwest"],char:"\u2199\ufe0f",fitzpatrick_scale:!1,category:"symbols"},arrow_upper_left:{keywords:["blue-square","point","direction","diagonal","northwest"],char:"\u2196\ufe0f",fitzpatrick_scale:!1,category:"symbols"},arrow_up_down:{keywords:["blue-square","direction","way","vertical"],char:"\u2195\ufe0f",fitzpatrick_scale:!1,category:"symbols"},left_right_arrow:{keywords:["shape","direction","horizontal","sideways"],char:"\u2194\ufe0f",fitzpatrick_scale:!1,category:"symbols"},arrows_counterclockwise:{keywords:["blue-square","sync","cycle"],char:"\u{1f504}",fitzpatrick_scale:!1,category:"symbols"},arrow_right_hook:{keywords:["blue-square","return","rotate","direction"],char:"\u21aa\ufe0f",fitzpatrick_scale:!1,category:"symbols"},leftwards_arrow_with_hook:{keywords:["back","return","blue-square","undo","enter"],char:"\u21a9\ufe0f",fitzpatrick_scale:!1,category:"symbols"},arrow_heading_up:{keywords:["blue-square","direction","top"],char:"\u2934\ufe0f",fitzpatrick_scale:!1,category:"symbols"},arrow_heading_down:{keywords:["blue-square","direction","bottom"],char:"\u2935\ufe0f",fitzpatrick_scale:!1,category:"symbols"},hash:{keywords:["symbol","blue-square","twitter"],char:"#\ufe0f\u20e3",fitzpatrick_scale:!1,category:"symbols"},information_source:{keywords:["blue-square","alphabet","letter"],char:"\u2139\ufe0f",fitzpatrick_scale:!1,category:"symbols"},abc:{keywords:["blue-square","alphabet"],char:"\u{1f524}",fitzpatrick_scale:!1,category:"symbols"},abcd:{keywords:["blue-square","alphabet"],char:"\u{1f521}",fitzpatrick_scale:!1,category:"symbols"},capital_abcd:{keywords:["alphabet","words","blue-square"],char:"\u{1f520}",fitzpatrick_scale:!1,category:"symbols"},symbols:{keywords:["blue-square","music","note","ampersand","percent","glyphs","characters"],char:"\u{1f523}",fitzpatrick_scale:!1,category:"symbols"},musical_note:{keywords:["score","tone","sound"],char:"\u{1f3b5}",fitzpatrick_scale:!1,category:"symbols"},notes:{keywords:["music","score"],char:"\u{1f3b6}",fitzpatrick_scale:!1,category:"symbols"},wavy_dash:{keywords:["draw","line","moustache","mustache","squiggle","scribble"],char:"\u3030\ufe0f",fitzpatrick_scale:!1,category:"symbols"},curly_loop:{keywords:["scribble","draw","shape","squiggle"],char:"\u27b0",fitzpatrick_scale:!1,category:"symbols"},heavy_check_mark:{keywords:["ok","nike","answer","yes","tick"],char:"\u2714\ufe0f",fitzpatrick_scale:!1,category:"symbols"},arrows_clockwise:{keywords:["sync","cycle","round","repeat"],char:"\u{1f503}",fitzpatrick_scale:!1,category:"symbols"},heavy_plus_sign:{keywords:["math","calculation","addition","more","increase"],char:"\u2795",fitzpatrick_scale:!1,category:"symbols"},heavy_minus_sign:{keywords:["math","calculation","subtract","less"],char:"\u2796",fitzpatrick_scale:!1,category:"symbols"},heavy_division_sign:{keywords:["divide","math","calculation"],char:"\u2797",fitzpatrick_scale:!1,category:"symbols"},heavy_multiplication_x:{keywords:["math","calculation"],char:"\u2716\ufe0f",fitzpatrick_scale:!1,category:"symbols"},infinity:{keywords:["forever"],char:"\u267e",fitzpatrick_scale:!1,category:"symbols"},heavy_dollar_sign:{keywords:["money","sales","payment","currency","buck"],char:"\u{1f4b2}",fitzpatrick_scale:!1,category:"symbols"},currency_exchange:{keywords:["money","sales","dollar","travel"],char:"\u{1f4b1}",fitzpatrick_scale:!1,category:"symbols"},copyright:{keywords:["ip","license","circle","law","legal"],char:"\xa9\ufe0f",fitzpatrick_scale:!1,category:"symbols"},registered:{keywords:["alphabet","circle"],char:"\xae\ufe0f",fitzpatrick_scale:!1,category:"symbols"},tm:{keywords:["trademark","brand","law","legal"],char:"\u2122\ufe0f",fitzpatrick_scale:!1,category:"symbols"},end:{keywords:["words","arrow"],char:"\u{1f51a}",fitzpatrick_scale:!1,category:"symbols"},back:{keywords:["arrow","words","return"],char:"\u{1f519}",fitzpatrick_scale:!1,category:"symbols"},on:{keywords:["arrow","words"],char:"\u{1f51b}",fitzpatrick_scale:!1,category:"symbols"},top:{keywords:["words","blue-square"],char:"\u{1f51d}",fitzpatrick_scale:!1,category:"symbols"},soon:{keywords:["arrow","words"],char:"\u{1f51c}",fitzpatrick_scale:!1,category:"symbols"},ballot_box_with_check:{keywords:["ok","agree","confirm","black-square","vote","election","yes","tick"],char:"\u2611\ufe0f",fitzpatrick_scale:!1,category:"symbols"},radio_button:{keywords:["input","old","music","circle"],char:"\u{1f518}",fitzpatrick_scale:!1,category:"symbols"},white_circle:{keywords:["shape","round"],char:"\u26aa",fitzpatrick_scale:!1,category:"symbols"},black_circle:{keywords:["shape","button","round"],char:"\u26ab",fitzpatrick_scale:!1,category:"symbols"},red_circle:{keywords:["shape","error","danger"],char:"\u{1f534}",fitzpatrick_scale:!1,category:"symbols"},large_blue_circle:{keywords:["shape","icon","button"],char:"\u{1f535}",fitzpatrick_scale:!1,category:"symbols"},small_orange_diamond:{keywords:["shape","jewel","gem"],char:"\u{1f538}",fitzpatrick_scale:!1,category:"symbols"},small_blue_diamond:{keywords:["shape","jewel","gem"],char:"\u{1f539}",fitzpatrick_scale:!1,category:"symbols"},large_orange_diamond:{keywords:["shape","jewel","gem"],char:"\u{1f536}",fitzpatrick_scale:!1,category:"symbols"},large_blue_diamond:{keywords:["shape","jewel","gem"],char:"\u{1f537}",fitzpatrick_scale:!1,category:"symbols"},small_red_triangle:{keywords:["shape","direction","up","top"],char:"\u{1f53a}",fitzpatrick_scale:!1,category:"symbols"},black_small_square:{keywords:["shape","icon"],char:"\u25aa\ufe0f",fitzpatrick_scale:!1,category:"symbols"},white_small_square:{keywords:["shape","icon"],char:"\u25ab\ufe0f",fitzpatrick_scale:!1,category:"symbols"},black_large_square:{keywords:["shape","icon","button"],char:"\u2b1b",fitzpatrick_scale:!1,category:"symbols"},white_large_square:{keywords:["shape","icon","stone","button"],char:"\u2b1c",fitzpatrick_scale:!1,category:"symbols"},small_red_triangle_down:{keywords:["shape","direction","bottom"],char:"\u{1f53b}",fitzpatrick_scale:!1,category:"symbols"},black_medium_square:{keywords:["shape","button","icon"],char:"\u25fc\ufe0f",fitzpatrick_scale:!1,category:"symbols"},white_medium_square:{keywords:["shape","stone","icon"],char:"\u25fb\ufe0f",fitzpatrick_scale:!1,category:"symbols"},black_medium_small_square:{keywords:["icon","shape","button"],char:"\u25fe",fitzpatrick_scale:!1,category:"symbols"},white_medium_small_square:{keywords:["shape","stone","icon","button"],char:"\u25fd",fitzpatrick_scale:!1,category:"symbols"},black_square_button:{keywords:["shape","input","frame"],char:"\u{1f532}",fitzpatrick_scale:!1,category:"symbols"},white_square_button:{keywords:["shape","input"],char:"\u{1f533}",fitzpatrick_scale:!1,category:"symbols"},speaker:{keywords:["sound","volume","silence","broadcast"],char:"\u{1f508}",fitzpatrick_scale:!1,category:"symbols"},sound:{keywords:["volume","speaker","broadcast"],char:"\u{1f509}",fitzpatrick_scale:!1,category:"symbols"},loud_sound:{keywords:["volume","noise","noisy","speaker","broadcast"],char:"\u{1f50a}",fitzpatrick_scale:!1,category:"symbols"},mute:{keywords:["sound","volume","silence","quiet"],char:"\u{1f507}",fitzpatrick_scale:!1,category:"symbols"},mega:{keywords:["sound","speaker","volume"],char:"\u{1f4e3}",fitzpatrick_scale:!1,category:"symbols"},loudspeaker:{keywords:["volume","sound"],char:"\u{1f4e2}",fitzpatrick_scale:!1,category:"symbols"},bell:{keywords:["sound","notification","christmas","xmas","chime"],char:"\u{1f514}",fitzpatrick_scale:!1,category:"symbols"},no_bell:{keywords:["sound","volume","mute","quiet","silent"],char:"\u{1f515}",fitzpatrick_scale:!1,category:"symbols"},black_joker:{keywords:["poker","cards","game","play","magic"],char:"\u{1f0cf}",fitzpatrick_scale:!1,category:"symbols"},mahjong:{keywords:["game","play","chinese","kanji"],char:"\u{1f004}",fitzpatrick_scale:!1,category:"symbols"},spades:{keywords:["poker","cards","suits","magic"],char:"\u2660\ufe0f",fitzpatrick_scale:!1,category:"symbols"},clubs:{keywords:["poker","cards","magic","suits"],char:"\u2663\ufe0f",fitzpatrick_scale:!1,category:"symbols"},hearts:{keywords:["poker","cards","magic","suits"],char:"\u2665\ufe0f",fitzpatrick_scale:!1,category:"symbols"},diamonds:{keywords:["poker","cards","magic","suits"],char:"\u2666\ufe0f",fitzpatrick_scale:!1,category:"symbols"},flower_playing_cards:{keywords:["game","sunset","red"],char:"\u{1f3b4}",fitzpatrick_scale:!1,category:"symbols"},thought_balloon:{keywords:["bubble","cloud","speech","thinking","dream"],char:"\u{1f4ad}",fitzpatrick_scale:!1,category:"symbols"},right_anger_bubble:{keywords:["caption","speech","thinking","mad"],char:"\u{1f5ef}",fitzpatrick_scale:!1,category:"symbols"},speech_balloon:{keywords:["bubble","words","message","talk","chatting"],char:"\u{1f4ac}",fitzpatrick_scale:!1,category:"symbols"},left_speech_bubble:{keywords:["words","message","talk","chatting"],char:"\u{1f5e8}",fitzpatrick_scale:!1,category:"symbols"},clock1:{keywords:["time","late","early","schedule"],char:"\u{1f550}",fitzpatrick_scale:!1,category:"symbols"},clock2:{keywords:["time","late","early","schedule"],char:"\u{1f551}",fitzpatrick_scale:!1,category:"symbols"},clock3:{keywords:["time","late","early","schedule"],char:"\u{1f552}",fitzpatrick_scale:!1,category:"symbols"},clock4:{keywords:["time","late","early","schedule"],char:"\u{1f553}",fitzpatrick_scale:!1,category:"symbols"},clock5:{keywords:["time","late","early","schedule"],char:"\u{1f554}",fitzpatrick_scale:!1,category:"symbols"},clock6:{keywords:["time","late","early","schedule","dawn","dusk"],char:"\u{1f555}",fitzpatrick_scale:!1,category:"symbols"},clock7:{keywords:["time","late","early","schedule"],char:"\u{1f556}",fitzpatrick_scale:!1,category:"symbols"},clock8:{keywords:["time","late","early","schedule"],char:"\u{1f557}",fitzpatrick_scale:!1,category:"symbols"},clock9:{keywords:["time","late","early","schedule"],char:"\u{1f558}",fitzpatrick_scale:!1,category:"symbols"},clock10:{keywords:["time","late","early","schedule"],char:"\u{1f559}",fitzpatrick_scale:!1,category:"symbols"},clock11:{keywords:["time","late","early","schedule"],char:"\u{1f55a}",fitzpatrick_scale:!1,category:"symbols"},clock12:{keywords:["time","noon","midnight","midday","late","early","schedule"],char:"\u{1f55b}",fitzpatrick_scale:!1,category:"symbols"},clock130:{keywords:["time","late","early","schedule"],char:"\u{1f55c}",fitzpatrick_scale:!1,category:"symbols"},clock230:{keywords:["time","late","early","schedule"],char:"\u{1f55d}",fitzpatrick_scale:!1,category:"symbols"},clock330:{keywords:["time","late","early","schedule"],char:"\u{1f55e}",fitzpatrick_scale:!1,category:"symbols"},clock430:{keywords:["time","late","early","schedule"],char:"\u{1f55f}",fitzpatrick_scale:!1,category:"symbols"},clock530:{keywords:["time","late","early","schedule"],char:"\u{1f560}",fitzpatrick_scale:!1,category:"symbols"},clock630:{keywords:["time","late","early","schedule"],char:"\u{1f561}",fitzpatrick_scale:!1,category:"symbols"},clock730:{keywords:["time","late","early","schedule"],char:"\u{1f562}",fitzpatrick_scale:!1,category:"symbols"},clock830:{keywords:["time","late","early","schedule"],char:"\u{1f563}",fitzpatrick_scale:!1,category:"symbols"},clock930:{keywords:["time","late","early","schedule"],char:"\u{1f564}",fitzpatrick_scale:!1,category:"symbols"},clock1030:{keywords:["time","late","early","schedule"],char:"\u{1f565}",fitzpatrick_scale:!1,category:"symbols"},clock1130:{keywords:["time","late","early","schedule"],char:"\u{1f566}",fitzpatrick_scale:!1,category:"symbols"},clock1230:{keywords:["time","late","early","schedule"],char:"\u{1f567}",fitzpatrick_scale:!1,category:"symbols"},afghanistan:{keywords:["af","flag","nation","country","banner"],char:"\u{1f1e6}\u{1f1eb}",fitzpatrick_scale:!1,category:"flags"},aland_islands:{keywords:["\xc5land","islands","flag","nation","country","banner"],char:"\u{1f1e6}\u{1f1fd}",fitzpatrick_scale:!1,category:"flags"},albania:{keywords:["al","flag","nation","country","banner"],char:"\u{1f1e6}\u{1f1f1}",fitzpatrick_scale:!1,category:"flags"},algeria:{keywords:["dz","flag","nation","country","banner"],char:"\u{1f1e9}\u{1f1ff}",fitzpatrick_scale:!1,category:"flags"},american_samoa:{keywords:["american","ws","flag","nation","country","banner"],char:"\u{1f1e6}\u{1f1f8}",fitzpatrick_scale:!1,category:"flags"},andorra:{keywords:["ad","flag","nation","country","banner"],char:"\u{1f1e6}\u{1f1e9}",fitzpatrick_scale:!1,category:"flags"},angola:{keywords:["ao","flag","nation","country","banner"],char:"\u{1f1e6}\u{1f1f4}",fitzpatrick_scale:!1,category:"flags"},anguilla:{keywords:["ai","flag","nation","country","banner"],char:"\u{1f1e6}\u{1f1ee}",fitzpatrick_scale:!1,category:"flags"},antarctica:{keywords:["aq","flag","nation","country","banner"],char:"\u{1f1e6}\u{1f1f6}",fitzpatrick_scale:!1,category:"flags"},antigua_barbuda:{keywords:["antigua","barbuda","flag","nation","country","banner"],char:"\u{1f1e6}\u{1f1ec}",fitzpatrick_scale:!1,category:"flags"},argentina:{keywords:["ar","flag","nation","country","banner"],char:"\u{1f1e6}\u{1f1f7}",fitzpatrick_scale:!1,category:"flags"},armenia:{keywords:["am","flag","nation","country","banner"],char:"\u{1f1e6}\u{1f1f2}",fitzpatrick_scale:!1,category:"flags"},aruba:{keywords:["aw","flag","nation","country","banner"],char:"\u{1f1e6}\u{1f1fc}",fitzpatrick_scale:!1,category:"flags"},australia:{keywords:["au","flag","nation","country","banner"],char:"\u{1f1e6}\u{1f1fa}",fitzpatrick_scale:!1,category:"flags"},austria:{keywords:["at","flag","nation","country","banner"],char:"\u{1f1e6}\u{1f1f9}",fitzpatrick_scale:!1,category:"flags"},azerbaijan:{keywords:["az","flag","nation","country","banner"],char:"\u{1f1e6}\u{1f1ff}",fitzpatrick_scale:!1,category:"flags"},bahamas:{keywords:["bs","flag","nation","country","banner"],char:"\u{1f1e7}\u{1f1f8}",fitzpatrick_scale:!1,category:"flags"},bahrain:{keywords:["bh","flag","nation","country","banner"],char:"\u{1f1e7}\u{1f1ed}",fitzpatrick_scale:!1,category:"flags"},bangladesh:{keywords:["bd","flag","nation","country","banner"],char:"\u{1f1e7}\u{1f1e9}",fitzpatrick_scale:!1,category:"flags"},barbados:{keywords:["bb","flag","nation","country","banner"],char:"\u{1f1e7}\u{1f1e7}",fitzpatrick_scale:!1,category:"flags"},belarus:{keywords:["by","flag","nation","country","banner"],char:"\u{1f1e7}\u{1f1fe}",fitzpatrick_scale:!1,category:"flags"},belgium:{keywords:["be","flag","nation","country","banner"],char:"\u{1f1e7}\u{1f1ea}",fitzpatrick_scale:!1,category:"flags"},belize:{keywords:["bz","flag","nation","country","banner"],char:"\u{1f1e7}\u{1f1ff}",fitzpatrick_scale:!1,category:"flags"},benin:{keywords:["bj","flag","nation","country","banner"],char:"\u{1f1e7}\u{1f1ef}",fitzpatrick_scale:!1,category:"flags"},bermuda:{keywords:["bm","flag","nation","country","banner"],char:"\u{1f1e7}\u{1f1f2}",fitzpatrick_scale:!1,category:"flags"},bhutan:{keywords:["bt","flag","nation","country","banner"],char:"\u{1f1e7}\u{1f1f9}",fitzpatrick_scale:!1,category:"flags"},bolivia:{keywords:["bo","flag","nation","country","banner"],char:"\u{1f1e7}\u{1f1f4}",fitzpatrick_scale:!1,category:"flags"},caribbean_netherlands:{keywords:["bonaire","flag","nation","country","banner"],char:"\u{1f1e7}\u{1f1f6}",fitzpatrick_scale:!1,category:"flags"},bosnia_herzegovina:{keywords:["bosnia","herzegovina","flag","nation","country","banner"],char:"\u{1f1e7}\u{1f1e6}",fitzpatrick_scale:!1,category:"flags"},botswana:{keywords:["bw","flag","nation","country","banner"],char:"\u{1f1e7}\u{1f1fc}",fitzpatrick_scale:!1,category:"flags"},brazil:{keywords:["br","flag","nation","country","banner"],char:"\u{1f1e7}\u{1f1f7}",fitzpatrick_scale:!1,category:"flags"},british_indian_ocean_territory:{keywords:["british","indian","ocean","territory","flag","nation","country","banner"],char:"\u{1f1ee}\u{1f1f4}",fitzpatrick_scale:!1,category:"flags"},british_virgin_islands:{keywords:["british","virgin","islands","bvi","flag","nation","country","banner"],char:"\u{1f1fb}\u{1f1ec}",fitzpatrick_scale:!1,category:"flags"},brunei:{keywords:["bn","darussalam","flag","nation","country","banner"],char:"\u{1f1e7}\u{1f1f3}",fitzpatrick_scale:!1,category:"flags"},bulgaria:{keywords:["bg","flag","nation","country","banner"],char:"\u{1f1e7}\u{1f1ec}",fitzpatrick_scale:!1,category:"flags"},burkina_faso:{keywords:["burkina","faso","flag","nation","country","banner"],char:"\u{1f1e7}\u{1f1eb}",fitzpatrick_scale:!1,category:"flags"},burundi:{keywords:["bi","flag","nation","country","banner"],char:"\u{1f1e7}\u{1f1ee}",fitzpatrick_scale:!1,category:"flags"},cape_verde:{keywords:["cabo","verde","flag","nation","country","banner"],char:"\u{1f1e8}\u{1f1fb}",fitzpatrick_scale:!1,category:"flags"},cambodia:{keywords:["kh","flag","nation","country","banner"],char:"\u{1f1f0}\u{1f1ed}",fitzpatrick_scale:!1,category:"flags"},cameroon:{keywords:["cm","flag","nation","country","banner"],char:"\u{1f1e8}\u{1f1f2}",fitzpatrick_scale:!1,category:"flags"},canada:{keywords:["ca","flag","nation","country","banner"],char:"\u{1f1e8}\u{1f1e6}",fitzpatrick_scale:!1,category:"flags"},canary_islands:{keywords:["canary","islands","flag","nation","country","banner"],char:"\u{1f1ee}\u{1f1e8}",fitzpatrick_scale:!1,category:"flags"},cayman_islands:{keywords:["cayman","islands","flag","nation","country","banner"],char:"\u{1f1f0}\u{1f1fe}",fitzpatrick_scale:!1,category:"flags"},central_african_republic:{keywords:["central","african","republic","flag","nation","country","banner"],char:"\u{1f1e8}\u{1f1eb}",fitzpatrick_scale:!1,category:"flags"},chad:{keywords:["td","flag","nation","country","banner"],char:"\u{1f1f9}\u{1f1e9}",fitzpatrick_scale:!1,category:"flags"},chile:{keywords:["flag","nation","country","banner"],char:"\u{1f1e8}\u{1f1f1}",fitzpatrick_scale:!1,category:"flags"},cn:{keywords:["china","chinese","prc","flag","country","nation","banner"],char:"\u{1f1e8}\u{1f1f3}",fitzpatrick_scale:!1,category:"flags"},christmas_island:{keywords:["christmas","island","flag","nation","country","banner"],char:"\u{1f1e8}\u{1f1fd}",fitzpatrick_scale:!1,category:"flags"},cocos_islands:{keywords:["cocos","keeling","islands","flag","nation","country","banner"],char:"\u{1f1e8}\u{1f1e8}",fitzpatrick_scale:!1,category:"flags"},colombia:{keywords:["co","flag","nation","country","banner"],char:"\u{1f1e8}\u{1f1f4}",fitzpatrick_scale:!1,category:"flags"},comoros:{keywords:["km","flag","nation","country","banner"],char:"\u{1f1f0}\u{1f1f2}",fitzpatrick_scale:!1,category:"flags"},congo_brazzaville:{keywords:["congo","flag","nation","country","banner"],char:"\u{1f1e8}\u{1f1ec}",fitzpatrick_scale:!1,category:"flags"},congo_kinshasa:{keywords:["congo","democratic","republic","flag","nation","country","banner"],char:"\u{1f1e8}\u{1f1e9}",fitzpatrick_scale:!1,category:"flags"},cook_islands:{keywords:["cook","islands","flag","nation","country","banner"],char:"\u{1f1e8}\u{1f1f0}",fitzpatrick_scale:!1,category:"flags"},costa_rica:{keywords:["costa","rica","flag","nation","country","banner"],char:"\u{1f1e8}\u{1f1f7}",fitzpatrick_scale:!1,category:"flags"},croatia:{keywords:["hr","flag","nation","country","banner"],char:"\u{1f1ed}\u{1f1f7}",fitzpatrick_scale:!1,category:"flags"},cuba:{keywords:["cu","flag","nation","country","banner"],char:"\u{1f1e8}\u{1f1fa}",fitzpatrick_scale:!1,category:"flags"},curacao:{keywords:["cura\xe7ao","flag","nation","country","banner"],char:"\u{1f1e8}\u{1f1fc}",fitzpatrick_scale:!1,category:"flags"},cyprus:{keywords:["cy","flag","nation","country","banner"],char:"\u{1f1e8}\u{1f1fe}",fitzpatrick_scale:!1,category:"flags"},czech_republic:{keywords:["cz","flag","nation","country","banner"],char:"\u{1f1e8}\u{1f1ff}",fitzpatrick_scale:!1,category:"flags"},denmark:{keywords:["dk","flag","nation","country","banner"],char:"\u{1f1e9}\u{1f1f0}",fitzpatrick_scale:!1,category:"flags"},djibouti:{keywords:["dj","flag","nation","country","banner"],char:"\u{1f1e9}\u{1f1ef}",fitzpatrick_scale:!1,category:"flags"},dominica:{keywords:["dm","flag","nation","country","banner"],char:"\u{1f1e9}\u{1f1f2}",fitzpatrick_scale:!1,category:"flags"},dominican_republic:{keywords:["dominican","republic","flag","nation","country","banner"],char:"\u{1f1e9}\u{1f1f4}",fitzpatrick_scale:!1,category:"flags"},ecuador:{keywords:["ec","flag","nation","country","banner"],char:"\u{1f1ea}\u{1f1e8}",fitzpatrick_scale:!1,category:"flags"},egypt:{keywords:["eg","flag","nation","country","banner"],char:"\u{1f1ea}\u{1f1ec}",fitzpatrick_scale:!1,category:"flags"},el_salvador:{keywords:["el","salvador","flag","nation","country","banner"],char:"\u{1f1f8}\u{1f1fb}",fitzpatrick_scale:!1,category:"flags"},equatorial_guinea:{keywords:["equatorial","gn","flag","nation","country","banner"],char:"\u{1f1ec}\u{1f1f6}",fitzpatrick_scale:!1,category:"flags"},eritrea:{keywords:["er","flag","nation","country","banner"],char:"\u{1f1ea}\u{1f1f7}",fitzpatrick_scale:!1,category:"flags"},estonia:{keywords:["ee","flag","nation","country","banner"],char:"\u{1f1ea}\u{1f1ea}",fitzpatrick_scale:!1,category:"flags"},ethiopia:{keywords:["et","flag","nation","country","banner"],char:"\u{1f1ea}\u{1f1f9}",fitzpatrick_scale:!1,category:"flags"},eu:{keywords:["european","union","flag","banner"],char:"\u{1f1ea}\u{1f1fa}",fitzpatrick_scale:!1,category:"flags"},falkland_islands:{keywords:["falkland","islands","malvinas","flag","nation","country","banner"],char:"\u{1f1eb}\u{1f1f0}",fitzpatrick_scale:!1,category:"flags"},faroe_islands:{keywords:["faroe","islands","flag","nation","country","banner"],char:"\u{1f1eb}\u{1f1f4}",fitzpatrick_scale:!1,category:"flags"},fiji:{keywords:["fj","flag","nation","country","banner"],char:"\u{1f1eb}\u{1f1ef}",fitzpatrick_scale:!1,category:"flags"},finland:{keywords:["fi","flag","nation","country","banner"],char:"\u{1f1eb}\u{1f1ee}",fitzpatrick_scale:!1,category:"flags"},fr:{keywords:["banner","flag","nation","france","french","country"],char:"\u{1f1eb}\u{1f1f7}",fitzpatrick_scale:!1,category:"flags"},french_guiana:{keywords:["french","guiana","flag","nation","country","banner"],char:"\u{1f1ec}\u{1f1eb}",fitzpatrick_scale:!1,category:"flags"},french_polynesia:{keywords:["french","polynesia","flag","nation","country","banner"],char:"\u{1f1f5}\u{1f1eb}",fitzpatrick_scale:!1,category:"flags"},french_southern_territories:{keywords:["french","southern","territories","flag","nation","country","banner"],char:"\u{1f1f9}\u{1f1eb}",fitzpatrick_scale:!1,category:"flags"},gabon:{keywords:["ga","flag","nation","country","banner"],char:"\u{1f1ec}\u{1f1e6}",fitzpatrick_scale:!1,category:"flags"},gambia:{keywords:["gm","flag","nation","country","banner"],char:"\u{1f1ec}\u{1f1f2}",fitzpatrick_scale:!1,category:"flags"},georgia:{keywords:["ge","flag","nation","country","banner"],char:"\u{1f1ec}\u{1f1ea}",fitzpatrick_scale:!1,category:"flags"},de:{keywords:["german","nation","flag","country","banner"],char:"\u{1f1e9}\u{1f1ea}",fitzpatrick_scale:!1,category:"flags"},ghana:{keywords:["gh","flag","nation","country","banner"],char:"\u{1f1ec}\u{1f1ed}",fitzpatrick_scale:!1,category:"flags"},gibraltar:{keywords:["gi","flag","nation","country","banner"],char:"\u{1f1ec}\u{1f1ee}",fitzpatrick_scale:!1,category:"flags"},greece:{keywords:["gr","flag","nation","country","banner"],char:"\u{1f1ec}\u{1f1f7}",fitzpatrick_scale:!1,category:"flags"},greenland:{keywords:["gl","flag","nation","country","banner"],char:"\u{1f1ec}\u{1f1f1}",fitzpatrick_scale:!1,category:"flags"},grenada:{keywords:["gd","flag","nation","country","banner"],char:"\u{1f1ec}\u{1f1e9}",fitzpatrick_scale:!1,category:"flags"},guadeloupe:{keywords:["gp","flag","nation","country","banner"],char:"\u{1f1ec}\u{1f1f5}",fitzpatrick_scale:!1,category:"flags"},guam:{keywords:["gu","flag","nation","country","banner"],char:"\u{1f1ec}\u{1f1fa}",fitzpatrick_scale:!1,category:"flags"},guatemala:{keywords:["gt","flag","nation","country","banner"],char:"\u{1f1ec}\u{1f1f9}",fitzpatrick_scale:!1,category:"flags"},guernsey:{keywords:["gg","flag","nation","country","banner"],char:"\u{1f1ec}\u{1f1ec}",fitzpatrick_scale:!1,category:"flags"},guinea:{keywords:["gn","flag","nation","country","banner"],char:"\u{1f1ec}\u{1f1f3}",fitzpatrick_scale:!1,category:"flags"},guinea_bissau:{keywords:["gw","bissau","flag","nation","country","banner"],char:"\u{1f1ec}\u{1f1fc}",fitzpatrick_scale:!1,category:"flags"},guyana:{keywords:["gy","flag","nation","country","banner"],char:"\u{1f1ec}\u{1f1fe}",fitzpatrick_scale:!1,category:"flags"},haiti:{keywords:["ht","flag","nation","country","banner"],char:"\u{1f1ed}\u{1f1f9}",fitzpatrick_scale:!1,category:"flags"},honduras:{keywords:["hn","flag","nation","country","banner"],char:"\u{1f1ed}\u{1f1f3}",fitzpatrick_scale:!1,category:"flags"},hong_kong:{keywords:["hong","kong","flag","nation","country","banner"],char:"\u{1f1ed}\u{1f1f0}",fitzpatrick_scale:!1,category:"flags"},hungary:{keywords:["hu","flag","nation","country","banner"],char:"\u{1f1ed}\u{1f1fa}",fitzpatrick_scale:!1,category:"flags"},iceland:{keywords:["is","flag","nation","country","banner"],char:"\u{1f1ee}\u{1f1f8}",fitzpatrick_scale:!1,category:"flags"},india:{keywords:["in","flag","nation","country","banner"],char:"\u{1f1ee}\u{1f1f3}",fitzpatrick_scale:!1,category:"flags"},indonesia:{keywords:["flag","nation","country","banner"],char:"\u{1f1ee}\u{1f1e9}",fitzpatrick_scale:!1,category:"flags"},iran:{keywords:["iran,","islamic","republic","flag","nation","country","banner"],char:"\u{1f1ee}\u{1f1f7}",fitzpatrick_scale:!1,category:"flags"},iraq:{keywords:["iq","flag","nation","country","banner"],char:"\u{1f1ee}\u{1f1f6}",fitzpatrick_scale:!1,category:"flags"},ireland:{keywords:["ie","flag","nation","country","banner"],char:"\u{1f1ee}\u{1f1ea}",fitzpatrick_scale:!1,category:"flags"},isle_of_man:{keywords:["isle","man","flag","nation","country","banner"],char:"\u{1f1ee}\u{1f1f2}",fitzpatrick_scale:!1,category:"flags"},israel:{keywords:["il","flag","nation","country","banner"],char:"\u{1f1ee}\u{1f1f1}",fitzpatrick_scale:!1,category:"flags"},it:{keywords:["italy","flag","nation","country","banner"],char:"\u{1f1ee}\u{1f1f9}",fitzpatrick_scale:!1,category:"flags"},cote_divoire:{keywords:["ivory","coast","flag","nation","country","banner"],char:"\u{1f1e8}\u{1f1ee}",fitzpatrick_scale:!1,category:"flags"},jamaica:{keywords:["jm","flag","nation","country","banner"],char:"\u{1f1ef}\u{1f1f2}",fitzpatrick_scale:!1,category:"flags"},jp:{keywords:["japanese","nation","flag","country","banner"],char:"\u{1f1ef}\u{1f1f5}",fitzpatrick_scale:!1,category:"flags"},jersey:{keywords:["je","flag","nation","country","banner"],char:"\u{1f1ef}\u{1f1ea}",fitzpatrick_scale:!1,category:"flags"},jordan:{keywords:["jo","flag","nation","country","banner"],char:"\u{1f1ef}\u{1f1f4}",fitzpatrick_scale:!1,category:"flags"},kazakhstan:{keywords:["kz","flag","nation","country","banner"],char:"\u{1f1f0}\u{1f1ff}",fitzpatrick_scale:!1,category:"flags"},kenya:{keywords:["ke","flag","nation","country","banner"],char:"\u{1f1f0}\u{1f1ea}",fitzpatrick_scale:!1,category:"flags"},kiribati:{keywords:["ki","flag","nation","country","banner"],char:"\u{1f1f0}\u{1f1ee}",fitzpatrick_scale:!1,category:"flags"},kosovo:{keywords:["xk","flag","nation","country","banner"],char:"\u{1f1fd}\u{1f1f0}",fitzpatrick_scale:!1,category:"flags"},kuwait:{keywords:["kw","flag","nation","country","banner"],char:"\u{1f1f0}\u{1f1fc}",fitzpatrick_scale:!1,category:"flags"},kyrgyzstan:{keywords:["kg","flag","nation","country","banner"],char:"\u{1f1f0}\u{1f1ec}",fitzpatrick_scale:!1,category:"flags"},laos:{keywords:["lao","democratic","republic","flag","nation","country","banner"],char:"\u{1f1f1}\u{1f1e6}",fitzpatrick_scale:!1,category:"flags"},latvia:{keywords:["lv","flag","nation","country","banner"],char:"\u{1f1f1}\u{1f1fb}",fitzpatrick_scale:!1,category:"flags"},lebanon:{keywords:["lb","flag","nation","country","banner"],char:"\u{1f1f1}\u{1f1e7}",fitzpatrick_scale:!1,category:"flags"},lesotho:{keywords:["ls","flag","nation","country","banner"],char:"\u{1f1f1}\u{1f1f8}",fitzpatrick_scale:!1,category:"flags"},liberia:{keywords:["lr","flag","nation","country","banner"],char:"\u{1f1f1}\u{1f1f7}",fitzpatrick_scale:!1,category:"flags"},libya:{keywords:["ly","flag","nation","country","banner"],char:"\u{1f1f1}\u{1f1fe}",fitzpatrick_scale:!1,category:"flags"},liechtenstein:{keywords:["li","flag","nation","country","banner"],char:"\u{1f1f1}\u{1f1ee}",fitzpatrick_scale:!1,category:"flags"},lithuania:{keywords:["lt","flag","nation","country","banner"],char:"\u{1f1f1}\u{1f1f9}",fitzpatrick_scale:!1,category:"flags"},luxembourg:{keywords:["lu","flag","nation","country","banner"],char:"\u{1f1f1}\u{1f1fa}",fitzpatrick_scale:!1,category:"flags"},macau:{keywords:["macao","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1f4}",fitzpatrick_scale:!1,category:"flags"},macedonia:{keywords:["macedonia,","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1f0}",fitzpatrick_scale:!1,category:"flags"},madagascar:{keywords:["mg","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1ec}",fitzpatrick_scale:!1,category:"flags"},malawi:{keywords:["mw","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1fc}",fitzpatrick_scale:!1,category:"flags"},malaysia:{keywords:["my","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1fe}",fitzpatrick_scale:!1,category:"flags"},maldives:{keywords:["mv","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1fb}",fitzpatrick_scale:!1,category:"flags"},mali:{keywords:["ml","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1f1}",fitzpatrick_scale:!1,category:"flags"},malta:{keywords:["mt","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1f9}",fitzpatrick_scale:!1,category:"flags"},marshall_islands:{keywords:["marshall","islands","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1ed}",fitzpatrick_scale:!1,category:"flags"},martinique:{keywords:["mq","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1f6}",fitzpatrick_scale:!1,category:"flags"},mauritania:{keywords:["mr","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1f7}",fitzpatrick_scale:!1,category:"flags"},mauritius:{keywords:["mu","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1fa}",fitzpatrick_scale:!1,category:"flags"},mayotte:{keywords:["yt","flag","nation","country","banner"],char:"\u{1f1fe}\u{1f1f9}",fitzpatrick_scale:!1,category:"flags"},mexico:{keywords:["mx","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1fd}",fitzpatrick_scale:!1,category:"flags"},micronesia:{keywords:["micronesia,","federated","states","flag","nation","country","banner"],char:"\u{1f1eb}\u{1f1f2}",fitzpatrick_scale:!1,category:"flags"},moldova:{keywords:["moldova,","republic","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1e9}",fitzpatrick_scale:!1,category:"flags"},monaco:{keywords:["mc","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1e8}",fitzpatrick_scale:!1,category:"flags"},mongolia:{keywords:["mn","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1f3}",fitzpatrick_scale:!1,category:"flags"},montenegro:{keywords:["me","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1ea}",fitzpatrick_scale:!1,category:"flags"},montserrat:{keywords:["ms","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1f8}",fitzpatrick_scale:!1,category:"flags"},morocco:{keywords:["ma","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1e6}",fitzpatrick_scale:!1,category:"flags"},mozambique:{keywords:["mz","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1ff}",fitzpatrick_scale:!1,category:"flags"},myanmar:{keywords:["mm","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1f2}",fitzpatrick_scale:!1,category:"flags"},namibia:{keywords:["na","flag","nation","country","banner"],char:"\u{1f1f3}\u{1f1e6}",fitzpatrick_scale:!1,category:"flags"},nauru:{keywords:["nr","flag","nation","country","banner"],char:"\u{1f1f3}\u{1f1f7}",fitzpatrick_scale:!1,category:"flags"},nepal:{keywords:["np","flag","nation","country","banner"],char:"\u{1f1f3}\u{1f1f5}",fitzpatrick_scale:!1,category:"flags"},netherlands:{keywords:["nl","flag","nation","country","banner"],char:"\u{1f1f3}\u{1f1f1}",fitzpatrick_scale:!1,category:"flags"},new_caledonia:{keywords:["new","caledonia","flag","nation","country","banner"],char:"\u{1f1f3}\u{1f1e8}",fitzpatrick_scale:!1,category:"flags"},new_zealand:{keywords:["new","zealand","flag","nation","country","banner"],char:"\u{1f1f3}\u{1f1ff}",fitzpatrick_scale:!1,category:"flags"},nicaragua:{keywords:["ni","flag","nation","country","banner"],char:"\u{1f1f3}\u{1f1ee}",fitzpatrick_scale:!1,category:"flags"},niger:{keywords:["ne","flag","nation","country","banner"],char:"\u{1f1f3}\u{1f1ea}",fitzpatrick_scale:!1,category:"flags"},nigeria:{keywords:["flag","nation","country","banner"],char:"\u{1f1f3}\u{1f1ec}",fitzpatrick_scale:!1,category:"flags"},niue:{keywords:["nu","flag","nation","country","banner"],char:"\u{1f1f3}\u{1f1fa}",fitzpatrick_scale:!1,category:"flags"},norfolk_island:{keywords:["norfolk","island","flag","nation","country","banner"],char:"\u{1f1f3}\u{1f1eb}",fitzpatrick_scale:!1,category:"flags"},northern_mariana_islands:{keywords:["northern","mariana","islands","flag","nation","country","banner"],char:"\u{1f1f2}\u{1f1f5}",fitzpatrick_scale:!1,category:"flags"},north_korea:{keywords:["north","korea","nation","flag","country","banner"],char:"\u{1f1f0}\u{1f1f5}",fitzpatrick_scale:!1,category:"flags"},norway:{keywords:["no","flag","nation","country","banner"],char:"\u{1f1f3}\u{1f1f4}",fitzpatrick_scale:!1,category:"flags"},oman:{keywords:["om_symbol","flag","nation","country","banner"],char:"\u{1f1f4}\u{1f1f2}",fitzpatrick_scale:!1,category:"flags"},pakistan:{keywords:["pk","flag","nation","country","banner"],char:"\u{1f1f5}\u{1f1f0}",fitzpatrick_scale:!1,category:"flags"},palau:{keywords:["pw","flag","nation","country","banner"],char:"\u{1f1f5}\u{1f1fc}",fitzpatrick_scale:!1,category:"flags"},palestinian_territories:{keywords:["palestine","palestinian","territories","flag","nation","country","banner"],char:"\u{1f1f5}\u{1f1f8}",fitzpatrick_scale:!1,category:"flags"},panama:{keywords:["pa","flag","nation","country","banner"],char:"\u{1f1f5}\u{1f1e6}",fitzpatrick_scale:!1,category:"flags"},papua_new_guinea:{keywords:["papua","new","guinea","flag","nation","country","banner"],char:"\u{1f1f5}\u{1f1ec}",fitzpatrick_scale:!1,category:"flags"},paraguay:{keywords:["py","flag","nation","country","banner"],char:"\u{1f1f5}\u{1f1fe}",fitzpatrick_scale:!1,category:"flags"},peru:{keywords:["pe","flag","nation","country","banner"],char:"\u{1f1f5}\u{1f1ea}",fitzpatrick_scale:!1,category:"flags"},philippines:{keywords:["ph","flag","nation","country","banner"],char:"\u{1f1f5}\u{1f1ed}",fitzpatrick_scale:!1,category:"flags"},pitcairn_islands:{keywords:["pitcairn","flag","nation","country","banner"],char:"\u{1f1f5}\u{1f1f3}",fitzpatrick_scale:!1,category:"flags"},poland:{keywords:["pl","flag","nation","country","banner"],char:"\u{1f1f5}\u{1f1f1}",fitzpatrick_scale:!1,category:"flags"},portugal:{keywords:["pt","flag","nation","country","banner"],char:"\u{1f1f5}\u{1f1f9}",fitzpatrick_scale:!1,category:"flags"},puerto_rico:{keywords:["puerto","rico","flag","nation","country","banner"],char:"\u{1f1f5}\u{1f1f7}",fitzpatrick_scale:!1,category:"flags"},qatar:{keywords:["qa","flag","nation","country","banner"],char:"\u{1f1f6}\u{1f1e6}",fitzpatrick_scale:!1,category:"flags"},reunion:{keywords:["r\xe9union","flag","nation","country","banner"],char:"\u{1f1f7}\u{1f1ea}",fitzpatrick_scale:!1,category:"flags"},romania:{keywords:["ro","flag","nation","country","banner"],char:"\u{1f1f7}\u{1f1f4}",fitzpatrick_scale:!1,category:"flags"},ru:{keywords:["russian","federation","flag","nation","country","banner"],char:"\u{1f1f7}\u{1f1fa}",fitzpatrick_scale:!1,category:"flags"},rwanda:{keywords:["rw","flag","nation","country","banner"],char:"\u{1f1f7}\u{1f1fc}",fitzpatrick_scale:!1,category:"flags"},st_barthelemy:{keywords:["saint","barth\xe9lemy","flag","nation","country","banner"],char:"\u{1f1e7}\u{1f1f1}",fitzpatrick_scale:!1,category:"flags"},st_helena:{keywords:["saint","helena","ascension","tristan","cunha","flag","nation","country","banner"],char:"\u{1f1f8}\u{1f1ed}",fitzpatrick_scale:!1,category:"flags"},st_kitts_nevis:{keywords:["saint","kitts","nevis","flag","nation","country","banner"],char:"\u{1f1f0}\u{1f1f3}",fitzpatrick_scale:!1,category:"flags"},st_lucia:{keywords:["saint","lucia","flag","nation","country","banner"],char:"\u{1f1f1}\u{1f1e8}",fitzpatrick_scale:!1,category:"flags"},st_pierre_miquelon:{keywords:["saint","pierre","miquelon","flag","nation","country","banner"],char:"\u{1f1f5}\u{1f1f2}",fitzpatrick_scale:!1,category:"flags"},st_vincent_grenadines:{keywords:["saint","vincent","grenadines","flag","nation","country","banner"],char:"\u{1f1fb}\u{1f1e8}",fitzpatrick_scale:!1,category:"flags"},samoa:{keywords:["ws","flag","nation","country","banner"],char:"\u{1f1fc}\u{1f1f8}",fitzpatrick_scale:!1,category:"flags"},san_marino:{keywords:["san","marino","flag","nation","country","banner"],char:"\u{1f1f8}\u{1f1f2}",fitzpatrick_scale:!1,category:"flags"},sao_tome_principe:{keywords:["sao","tome","principe","flag","nation","country","banner"],char:"\u{1f1f8}\u{1f1f9}",fitzpatrick_scale:!1,category:"flags"},saudi_arabia:{keywords:["flag","nation","country","banner"],char:"\u{1f1f8}\u{1f1e6}",fitzpatrick_scale:!1,category:"flags"},senegal:{keywords:["sn","flag","nation","country","banner"],char:"\u{1f1f8}\u{1f1f3}",fitzpatrick_scale:!1,category:"flags"},serbia:{keywords:["rs","flag","nation","country","banner"],char:"\u{1f1f7}\u{1f1f8}",fitzpatrick_scale:!1,category:"flags"},seychelles:{keywords:["sc","flag","nation","country","banner"],char:"\u{1f1f8}\u{1f1e8}",fitzpatrick_scale:!1,category:"flags"},sierra_leone:{keywords:["sierra","leone","flag","nation","country","banner"],char:"\u{1f1f8}\u{1f1f1}",fitzpatrick_scale:!1,category:"flags"},singapore:{keywords:["sg","flag","nation","country","banner"],char:"\u{1f1f8}\u{1f1ec}",fitzpatrick_scale:!1,category:"flags"},sint_maarten:{keywords:["sint","maarten","dutch","flag","nation","country","banner"],char:"\u{1f1f8}\u{1f1fd}",fitzpatrick_scale:!1,category:"flags"},slovakia:{keywords:["sk","flag","nation","country","banner"],char:"\u{1f1f8}\u{1f1f0}",fitzpatrick_scale:!1,category:"flags"},slovenia:{keywords:["si","flag","nation","country","banner"],char:"\u{1f1f8}\u{1f1ee}",fitzpatrick_scale:!1,category:"flags"},solomon_islands:{keywords:["solomon","islands","flag","nation","country","banner"],char:"\u{1f1f8}\u{1f1e7}",fitzpatrick_scale:!1,category:"flags"},somalia:{keywords:["so","flag","nation","country","banner"],char:"\u{1f1f8}\u{1f1f4}",fitzpatrick_scale:!1,category:"flags"},south_africa:{keywords:["south","africa","flag","nation","country","banner"],char:"\u{1f1ff}\u{1f1e6}",fitzpatrick_scale:!1,category:"flags"},south_georgia_south_sandwich_islands:{keywords:["south","georgia","sandwich","islands","flag","nation","country","banner"],char:"\u{1f1ec}\u{1f1f8}",fitzpatrick_scale:!1,category:"flags"},kr:{keywords:["south","korea","nation","flag","country","banner"],char:"\u{1f1f0}\u{1f1f7}",fitzpatrick_scale:!1,category:"flags"},south_sudan:{keywords:["south","sd","flag","nation","country","banner"],char:"\u{1f1f8}\u{1f1f8}",fitzpatrick_scale:!1,category:"flags"},es:{keywords:["spain","flag","nation","country","banner"],char:"\u{1f1ea}\u{1f1f8}",fitzpatrick_scale:!1,category:"flags"},sri_lanka:{keywords:["sri","lanka","flag","nation","country","banner"],char:"\u{1f1f1}\u{1f1f0}",fitzpatrick_scale:!1,category:"flags"},sudan:{keywords:["sd","flag","nation","country","banner"],char:"\u{1f1f8}\u{1f1e9}",fitzpatrick_scale:!1,category:"flags"},suriname:{keywords:["sr","flag","nation","country","banner"],char:"\u{1f1f8}\u{1f1f7}",fitzpatrick_scale:!1,category:"flags"},swaziland:{keywords:["sz","flag","nation","country","banner"],char:"\u{1f1f8}\u{1f1ff}",fitzpatrick_scale:!1,category:"flags"},sweden:{keywords:["se","flag","nation","country","banner"],char:"\u{1f1f8}\u{1f1ea}",fitzpatrick_scale:!1,category:"flags"},switzerland:{keywords:["ch","flag","nation","country","banner"],char:"\u{1f1e8}\u{1f1ed}",fitzpatrick_scale:!1,category:"flags"},syria:{keywords:["syrian","arab","republic","flag","nation","country","banner"],char:"\u{1f1f8}\u{1f1fe}",fitzpatrick_scale:!1,category:"flags"},taiwan:{keywords:["tw","flag","nation","country","banner"],char:"\u{1f1f9}\u{1f1fc}",fitzpatrick_scale:!1,category:"flags"},tajikistan:{keywords:["tj","flag","nation","country","banner"],char:"\u{1f1f9}\u{1f1ef}",fitzpatrick_scale:!1,category:"flags"},tanzania:{keywords:["tanzania,","united","republic","flag","nation","country","banner"],char:"\u{1f1f9}\u{1f1ff}",fitzpatrick_scale:!1,category:"flags"},thailand:{keywords:["th","flag","nation","country","banner"],char:"\u{1f1f9}\u{1f1ed}",fitzpatrick_scale:!1,category:"flags"},timor_leste:{keywords:["timor","leste","flag","nation","country","banner"],char:"\u{1f1f9}\u{1f1f1}",fitzpatrick_scale:!1,category:"flags"},togo:{keywords:["tg","flag","nation","country","banner"],char:"\u{1f1f9}\u{1f1ec}",fitzpatrick_scale:!1,category:"flags"},tokelau:{keywords:["tk","flag","nation","country","banner"],char:"\u{1f1f9}\u{1f1f0}",fitzpatrick_scale:!1,category:"flags"},tonga:{keywords:["to","flag","nation","country","banner"],char:"\u{1f1f9}\u{1f1f4}",fitzpatrick_scale:!1,category:"flags"},trinidad_tobago:{keywords:["trinidad","tobago","flag","nation","country","banner"],char:"\u{1f1f9}\u{1f1f9}",fitzpatrick_scale:!1,category:"flags"},tunisia:{keywords:["tn","flag","nation","country","banner"],char:"\u{1f1f9}\u{1f1f3}",fitzpatrick_scale:!1,category:"flags"},tr:{keywords:["turkey","flag","nation","country","banner"],char:"\u{1f1f9}\u{1f1f7}",fitzpatrick_scale:!1,category:"flags"},turkmenistan:{keywords:["flag","nation","country","banner"],char:"\u{1f1f9}\u{1f1f2}",fitzpatrick_scale:!1,category:"flags"},turks_caicos_islands:{keywords:["turks","caicos","islands","flag","nation","country","banner"],char:"\u{1f1f9}\u{1f1e8}",fitzpatrick_scale:!1,category:"flags"},tuvalu:{keywords:["flag","nation","country","banner"],char:"\u{1f1f9}\u{1f1fb}",fitzpatrick_scale:!1,category:"flags"},uganda:{keywords:["ug","flag","nation","country","banner"],char:"\u{1f1fa}\u{1f1ec}",fitzpatrick_scale:!1,category:"flags"},ukraine:{keywords:["ua","flag","nation","country","banner"],char:"\u{1f1fa}\u{1f1e6}",fitzpatrick_scale:!1,category:"flags"},united_arab_emirates:{keywords:["united","arab","emirates","flag","nation","country","banner"],char:"\u{1f1e6}\u{1f1ea}",fitzpatrick_scale:!1,category:"flags"},uk:{keywords:["united","kingdom","great","britain","northern","ireland","flag","nation","country","banner","british","UK","english","england","union jack"],char:"\u{1f1ec}\u{1f1e7}",fitzpatrick_scale:!1,category:"flags"},england:{keywords:["flag","english"],char:"\u{1f3f4}\u{e0067}\u{e0062}\u{e0065}\u{e006e}\u{e0067}\u{e007f}",fitzpatrick_scale:!1,category:"flags"},scotland:{keywords:["flag","scottish"],char:"\u{1f3f4}\u{e0067}\u{e0062}\u{e0073}\u{e0063}\u{e0074}\u{e007f}",fitzpatrick_scale:!1,category:"flags"},wales:{keywords:["flag","welsh"],char:"\u{1f3f4}\u{e0067}\u{e0062}\u{e0077}\u{e006c}\u{e0073}\u{e007f}",fitzpatrick_scale:!1,category:"flags"},us:{keywords:["united","states","america","flag","nation","country","banner"],char:"\u{1f1fa}\u{1f1f8}",fitzpatrick_scale:!1,category:"flags"},us_virgin_islands:{keywords:["virgin","islands","us","flag","nation","country","banner"],char:"\u{1f1fb}\u{1f1ee}",fitzpatrick_scale:!1,category:"flags"},uruguay:{keywords:["uy","flag","nation","country","banner"],char:"\u{1f1fa}\u{1f1fe}",fitzpatrick_scale:!1,category:"flags"},uzbekistan:{keywords:["uz","flag","nation","country","banner"],char:"\u{1f1fa}\u{1f1ff}",fitzpatrick_scale:!1,category:"flags"},vanuatu:{keywords:["vu","flag","nation","country","banner"],char:"\u{1f1fb}\u{1f1fa}",fitzpatrick_scale:!1,category:"flags"},vatican_city:{keywords:["vatican","city","flag","nation","country","banner"],char:"\u{1f1fb}\u{1f1e6}",fitzpatrick_scale:!1,category:"flags"},venezuela:{keywords:["ve","bolivarian","republic","flag","nation","country","banner"],char:"\u{1f1fb}\u{1f1ea}",fitzpatrick_scale:!1,category:"flags"},vietnam:{keywords:["viet","nam","flag","nation","country","banner"],char:"\u{1f1fb}\u{1f1f3}",fitzpatrick_scale:!1,category:"flags"},wallis_futuna:{keywords:["wallis","futuna","flag","nation","country","banner"],char:"\u{1f1fc}\u{1f1eb}",fitzpatrick_scale:!1,category:"flags"},western_sahara:{keywords:["western","sahara","flag","nation","country","banner"],char:"\u{1f1ea}\u{1f1ed}",fitzpatrick_scale:!1,category:"flags"},yemen:{keywords:["ye","flag","nation","country","banner"],char:"\u{1f1fe}\u{1f1ea}",fitzpatrick_scale:!1,category:"flags"},zambia:{keywords:["zm","flag","nation","country","banner"],char:"\u{1f1ff}\u{1f1f2}",fitzpatrick_scale:!1,category:"flags"},zimbabwe:{keywords:["zw","flag","nation","country","banner"],char:"\u{1f1ff}\u{1f1fc}",fitzpatrick_scale:!1,category:"flags"},united_nations:{keywords:["un","flag","banner"],char:"\u{1f1fa}\u{1f1f3}",fitzpatrick_scale:!1,category:"flags"},pirate_flag:{keywords:["skull","crossbones","flag","banner"],char:"\u{1f3f4}\u200d\u2620\ufe0f",fitzpatrick_scale:!1,category:"flags"}}); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/emoticons/plugin.min.js b/apps/web-antd/public/tinymce/plugins/emoticons/plugin.min.js new file mode 100644 index 0000000..988cd6c --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/emoticons/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";var t=tinymce.util.Tools.resolve("tinymce.PluginManager");const e=t=>e=>t===e,o=e(null),n=e(void 0),s=()=>{},r=()=>!1;class a{constructor(t,e){this.tag=t,this.value=e}static some(t){return new a(!0,t)}static none(){return a.singletonNone}fold(t,e){return this.tag?e(this.value):t()}isSome(){return this.tag}isNone(){return!this.tag}map(t){return this.tag?a.some(t(this.value)):a.none()}bind(t){return this.tag?t(this.value):a.none()}exists(t){return this.tag&&t(this.value)}forall(t){return!this.tag||t(this.value)}filter(t){return!this.tag||t(this.value)?this:a.none()}getOr(t){return this.tag?this.value:t}or(t){return this.tag?this:t}getOrThunk(t){return this.tag?this.value:t()}orThunk(t){return this.tag?this:t()}getOrDie(t){if(this.tag)return this.value;throw new Error(null!=t?t:"Called getOrDie on None")}static from(t){return null==t?a.none():a.some(t)}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(t){this.tag&&t(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}a.singletonNone=new a(!1);const i=(t,e)=>{const o=t.length,n=new Array(o);for(let s=0;s{let e=t;return{get:()=>e,set:t=>{e=t}}},c=Object.keys,u=Object.hasOwnProperty,g=(t,e)=>{const o=c(t);for(let n=0,s=o.length;nu.call(t,e),m=(h=(t,e)=>e,(...t)=>{if(0===t.length)throw new Error("Can't merge zero objects");const e={};for(let o=0;o{const t=(t=>{const e=l(a.none()),o=()=>e.get().each(t);return{clear:()=>{o(),e.set(a.none())},isSet:()=>e.get().isSome(),get:()=>e.get(),set:t=>{o(),e.set(a.some(t))}}})(s);return{...t,on:e=>t.get().each(e)}},y=(t,e,o=0,s)=>{const r=t.indexOf(e,o);return-1!==r&&(!!n(s)||r+e.length<=s)};var v=tinymce.util.Tools.resolve("tinymce.Resource");const f=t=>e=>e.options.get(t),b=f("emoticons_database"),w=f("emoticons_database_url"),j=f("emoticons_database_id"),C=f("emoticons_append"),_=f("emoticons_images_url"),A="All",k={symbols:"Symbols",people:"People",animals_and_nature:"Animals and Nature",food_and_drink:"Food and Drink",activity:"Activity",travel_and_places:"Travel and Places",objects:"Objects",flags:"Flags",user:"User Defined"},O=(t,e)=>d(t,e)?t[e]:e,x=t=>{const e=C(t);return o=t=>({keywords:[],category:"user",...t}),((t,e)=>{const o={};return g(t,((t,n)=>{const s=e(t,n);o[s.k]=s.v})),o})(e,((t,e)=>({k:e,v:o(t)})));var o},E=(t,e)=>y(t.title.toLowerCase(),e)||((t,o)=>{for(let o=0,s=t.length;o{const n=[],s=e.toLowerCase(),a=o.fold((()=>r),(t=>e=>e>=t));for(let o=0;o{const n={pattern:"",results:L(e.listAll(),"",a.some(300))},s=l(A),r=((t,e)=>{let n=null;const s=()=>{o(n)||(clearTimeout(n),n=null)};return{cancel:s,throttle:(...e)=>{s(),n=setTimeout((()=>{n=null,t.apply(null,e)}),200)}}})((t=>{(t=>{const o=t.getData(),n=s.get(),r=e.listCategory(n),i=L(r,o[S],n===A?a.some(300):a.none());t.setData({results:i})})(t)})),c={label:"Search",type:"input",name:S},u={type:"collection",name:"results"},g=()=>({title:"Emojis",size:"normal",body:{type:"tabpanel",tabs:i(e.listCategories(),(t=>({title:t,name:t,items:[c,u]})))},initialData:n,onTabChange:(t,e)=>{s.set(e.newTabName),r.throttle(t)},onChange:r.throttle,onAction:(e,o)=>{"results"===o.name&&(((t,e)=>{t.insertContent(e)})(t,o.value),e.close())},buttons:[{type:"cancel",text:"Close",primary:!0}]}),d=t.windowManager.open(g());d.focus(S),e.hasLoaded()||(d.block("Loading emojis..."),e.waitForLoad().then((()=>{d.redial(g()),r.throttle(d),d.focus(S),d.unblock()})).catch((t=>{d.redial({title:"Emojis",body:{type:"panel",items:[{type:"alertbanner",level:"error",icon:"warning",text:"Could not load emojis"}]},buttons:[{type:"cancel",text:"Close",primary:!0}],initialData:{pattern:"",results:[]}}),d.focus(S),d.unblock()})))},T=t=>e=>{const o=()=>{e.setEnabled(t.selection.isEditable())};return t.on("NodeChange",o),o(),()=>{t.off("NodeChange",o)}};t.add("emoticons",((t,e)=>{((t,e)=>{const o=t.options.register;o("emoticons_database",{processor:"string",default:"emojis"}),o("emoticons_database_url",{processor:"string",default:`${e}/js/${b(t)}${t.suffix}.js`}),o("emoticons_database_id",{processor:"string",default:"tinymce.plugins.emoticons"}),o("emoticons_append",{processor:"object",default:{}}),o("emoticons_images_url",{processor:"string",default:"https://cdnjs.cloudflare.com/ajax/libs/twemoji/15.1.0/72x72/"})})(t,e);const o=((t,e,o)=>{const n=p(),s=p(),r=_(t),i=t=>{return o="=4&&e.substr(0,4)===o?t.char.replace(/src="([^"]+)"/,((t,e)=>`src="${r}${e}"`)):t.char;var e,o};t.on("init",(()=>{v.load(o,e).then((e=>{const o=x(t);(t=>{const e={},o=[];g(t,((t,n)=>{const s={title:n,keywords:t.keywords,char:i(t),category:O(k,t.category)},r=void 0!==e[s.category]?e[s.category]:[];e[s.category]=r.concat([s]),o.push(s)})),n.set(e),s.set(o)})(m(e,o))}),(t=>{console.log(`Failed to load emojis: ${t}`),n.set({}),s.set([])}))}));const l=()=>s.get().getOr([]),u=()=>n.isSet()&&s.isSet();return{listCategories:()=>[A].concat(c(n.get().getOr({}))),hasLoaded:u,waitForLoad:()=>u()?Promise.resolve(!0):new Promise(((t,o)=>{let n=15;const s=setInterval((()=>{u()?(clearInterval(s),t(!0)):(n--,n<0&&(console.log("Could not load emojis from url: "+e),clearInterval(s),o(!1)))}),100)})),listAll:l,listCategory:t=>t===A?l():n.get().bind((e=>a.from(e[t]))).getOr([])}})(t,w(t),j(t));return((t,e)=>{t.addCommand("mceEmoticons",(()=>N(t,e)))})(t,o),(t=>{const e=()=>t.execCommand("mceEmoticons");t.ui.registry.addButton("emoticons",{tooltip:"Emojis",icon:"emoji",onAction:e,onSetup:T(t)}),t.ui.registry.addMenuItem("emoticons",{text:"Emojis...",icon:"emoji",onAction:e,onSetup:T(t)})})(t),((t,e)=>{t.ui.registry.addAutocompleter("emoticons",{trigger:":",columns:"auto",minChars:2,fetch:(t,o)=>e.waitForLoad().then((()=>{const n=e.listAll();return L(n,t,a.some(o))})),onAction:(e,o,n)=>{t.selection.setRng(o),t.insertContent(n),e.hide()}})})(t,o),(t=>{t.on("PreInit",(()=>{t.parser.addAttributeFilter("data-emoticon",(t=>{((t,e)=>{for(let e=0,n=t.length;eo.waitForLoad().then((()=>o.listAll()))}}))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/fullscreen/plugin.min.js b/apps/web-antd/public/tinymce/plugins/fullscreen/plugin.min.js new file mode 100644 index 0000000..18a522c --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/fullscreen/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";const e=e=>{let t=e;return{get:()=>t,set:e=>{t=e}}};var t=tinymce.util.Tools.resolve("tinymce.PluginManager");const n=e=>t=>(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(n=r=e,(o=String).prototype.isPrototypeOf(n)||(null===(s=r.constructor)||void 0===s?void 0:s.name)===o.name)?"string":t;var n,r,o,s})(t)===e,r=e=>t=>typeof t===e,o=e=>t=>e===t,s=n("string"),i=n("object"),l=n("array"),a=o(null),c=r("boolean"),u=o(void 0),d=e=>!(e=>null==e)(e),m=r("function"),h=r("number"),g=()=>{},p=e=>()=>e;function f(e,...t){return(...n)=>{const r=t.concat(n);return e.apply(null,r)}}const v=p(!1),w=p(!0);class y{constructor(e,t){this.tag=e,this.value=t}static some(e){return new y(!0,e)}static none(){return y.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?y.some(e(this.value)):y.none()}bind(e){return this.tag?e(this.value):y.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:y.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return d(e)?y.some(e):y.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}y.singletonNone=new y(!1);const b=Array.prototype.push,S=(e,t)=>{const n=e.length,r=new Array(n);for(let o=0;o{for(let n=0,r=e.length;n{const n=[];for(let r=0,o=e.length;r((e,t,n)=>{for(let r=0,o=e.length;r{const n=e(y.none()),r=()=>n.get().each(t);return{clear:()=>{r(),n.set(y.none())},isSet:()=>n.get().isSome(),get:()=>n.get(),set:e=>{r(),n.set(y.some(e))}}},k=()=>O((e=>e.unbind())),T=Object.keys,C="undefined"!=typeof window?window:Function("return this;")(),A=(e,t)=>((e,t)=>{let n=null!=t?t:C;for(let t=0;t{const t=A("ownerDocument.defaultView",e);return i(e)&&((e=>((e,t)=>{const n=((e,t)=>A(e,t))(e,t);if(null==n)throw new Error(e+" not available on this browser");return n})("HTMLElement",e))(t).prototype.isPrototypeOf(e)||/^HTML\w*Element$/.test(R(e).constructor.name))},M=e=>t=>(e=>e.dom.nodeType)(t)===e,P=M(1),D=M(3),N=M(9),H=M(11),V=(e,t)=>{const n=e.dom.getAttribute(t);return null===n?void 0:n},W=(e,t)=>{e.dom.removeAttribute(t)},q=(e,t,n=0,r)=>{const o=e.indexOf(t,n);return-1!==o&&(!!u(r)||o+t.length<=r)},B=e=>void 0!==e.style&&m(e.style.getPropertyValue),I=e=>{if(null==e)throw new Error("Node cannot be null or undefined");return{dom:e}},j=I,_=(e,t)=>{const n=e.dom;if(1!==n.nodeType)return!1;{const e=n;if(void 0!==e.matches)return e.matches(t);if(void 0!==e.msMatchesSelector)return e.msMatchesSelector(t);if(void 0!==e.webkitMatchesSelector)return e.webkitMatchesSelector(t);if(void 0!==e.mozMatchesSelector)return e.mozMatchesSelector(t);throw new Error("Browser lacks native selectors")}},z=e=>j(e.dom.ownerDocument),K=e=>S(e.dom.childNodes,j),$=m(Element.prototype.attachShadow)&&m(Node.prototype.getRootNode),U=p($),X=$?e=>j(e.dom.getRootNode()):e=>N(e)?e:z(e),Y=e=>{const t=X(e);return H(n=t)&&d(n.dom.host)?y.some(t):y.none();var n},G=e=>j(e.dom.host),J=e=>{const t=D(e)?e.dom.parentNode:e.dom;if(null==t||null===t.ownerDocument)return!1;const n=t.ownerDocument;return Y(j(t)).fold((()=>n.body.contains(t)),(r=J,o=G,e=>r(o(e))));var r,o},Q=(e,t,n)=>{if(!s(n))throw console.error("Invalid call to CSS.set. Property ",t,":: Value ",n,":: Element ",e),new Error("CSS value must be a string: "+n);B(e)&&e.style.setProperty(t,n)},Z=(e,t,n)=>{const r=e.dom;Q(r,t,n)},ee=(e,t)=>{const n=e.dom;((e,t)=>{const n=T(e);for(let r=0,o=n.length;r{Q(n,t,e)}))},te=(e,t)=>{const n=e.dom,r=window.getComputedStyle(n).getPropertyValue(t);return""!==r||J(e)?r:ne(n,t)},ne=(e,t)=>B(e)?e.style.getPropertyValue(t):"",re=e=>{const t=j((e=>{if(U()&&d(e.target)){const t=j(e.target);if(P(t)&&d(t.dom.shadowRoot)&&e.composed&&e.composedPath){const t=e.composedPath();if(t)return((e,t)=>0e.stopPropagation(),r=()=>e.preventDefault(),o=(s=r,i=n,(...e)=>s(i.apply(null,e)));var s,i;return((e,t,n,r,o,s,i)=>({target:e,x:t,y:n,stop:r,prevent:o,kill:s,raw:i}))(t,e.clientX,e.clientY,n,r,o,e)},oe=(e,t,n,r)=>{e.dom.removeEventListener(t,n,r)},se=w,ie=(e,t,n)=>((e,t,n,r)=>((e,t,n,r,o)=>{const s=((e,t)=>n=>{e(n)&&t(re(n))})(n,r);return e.dom.addEventListener(t,s,o),{unbind:f(oe,e,t,s,o)}})(e,t,n,r,!1))(e,t,se,n),le=()=>ae(0,0),ae=(e,t)=>({major:e,minor:t}),ce={nu:ae,detect:(e,t)=>{const n=String(t).toLowerCase();return 0===e.length?le():((e,t)=>{const n=((e,t)=>{for(let n=0;nNumber(t.replace(n,"$"+e));return ae(r(1),r(2))})(e,n)},unknown:le},ue=(e,t)=>{const n=String(t).toLowerCase();return F(e,(e=>e.search(n)))},de=/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,me=e=>t=>q(t,e),he=[{name:"Edge",versionRegexes:[/.*?edge\/ ?([0-9]+)\.([0-9]+)$/],search:e=>q(e,"edge/")&&q(e,"chrome")&&q(e,"safari")&&q(e,"applewebkit")},{name:"Chromium",brand:"Chromium",versionRegexes:[/.*?chrome\/([0-9]+)\.([0-9]+).*/,de],search:e=>q(e,"chrome")&&!q(e,"chromeframe")},{name:"IE",versionRegexes:[/.*?msie\ ?([0-9]+)\.([0-9]+).*/,/.*?rv:([0-9]+)\.([0-9]+).*/],search:e=>q(e,"msie")||q(e,"trident")},{name:"Opera",versionRegexes:[de,/.*?opera\/([0-9]+)\.([0-9]+).*/],search:me("opera")},{name:"Firefox",versionRegexes:[/.*?firefox\/\ ?([0-9]+)\.([0-9]+).*/],search:me("firefox")},{name:"Safari",versionRegexes:[de,/.*?cpu os ([0-9]+)_([0-9]+).*/],search:e=>(q(e,"safari")||q(e,"mobile/"))&&q(e,"applewebkit")}],ge=[{name:"Windows",search:me("win"),versionRegexes:[/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/]},{name:"iOS",search:e=>q(e,"iphone")||q(e,"ipad"),versionRegexes:[/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,/.*cpu os ([0-9]+)_([0-9]+).*/,/.*cpu iphone os ([0-9]+)_([0-9]+).*/]},{name:"Android",search:me("android"),versionRegexes:[/.*?android\ ?([0-9]+)\.([0-9]+).*/]},{name:"macOS",search:me("mac os x"),versionRegexes:[/.*?mac\ os\ x\ ?([0-9]+)_([0-9]+).*/]},{name:"Linux",search:me("linux"),versionRegexes:[]},{name:"Solaris",search:me("sunos"),versionRegexes:[]},{name:"FreeBSD",search:me("freebsd"),versionRegexes:[]},{name:"ChromeOS",search:me("cros"),versionRegexes:[/.*?chrome\/([0-9]+)\.([0-9]+).*/]}],pe={browsers:p(he),oses:p(ge)},fe="Edge",ve="Chromium",we="Opera",ye="Firefox",be="Safari",Se=e=>{const t=e.current,n=e.version,r=e=>()=>t===e;return{current:t,version:n,isEdge:r(fe),isChromium:r(ve),isIE:r("IE"),isOpera:r(we),isFirefox:r(ye),isSafari:r(be)}},xe=()=>Se({current:void 0,version:ce.unknown()}),Ee=Se,Fe=(p(fe),p(ve),p("IE"),p(we),p(ye),p(be),"Windows"),Oe="Android",ke="Linux",Te="macOS",Ce="Solaris",Ae="FreeBSD",Re="ChromeOS",Le=e=>{const t=e.current,n=e.version,r=e=>()=>t===e;return{current:t,version:n,isWindows:r(Fe),isiOS:r("iOS"),isAndroid:r(Oe),isMacOS:r(Te),isLinux:r(ke),isSolaris:r(Ce),isFreeBSD:r(Ae),isChromeOS:r(Re)}},Me=()=>Le({current:void 0,version:ce.unknown()}),Pe=Le,De=(p(Fe),p("iOS"),p(Oe),p(ke),p(Te),p(Ce),p(Ae),p(Re),(e,t,n)=>{const r=pe.browsers(),o=pe.oses(),s=t.bind((e=>((e,t)=>((e,t)=>{for(let n=0;n{const n=t.brand.toLowerCase();return F(e,(e=>{var t;return n===(null===(t=e.brand)||void 0===t?void 0:t.toLowerCase())})).map((e=>({current:e.name,version:ce.nu(parseInt(t.version,10),0)})))})))(r,e))).orThunk((()=>((e,t)=>ue(e,t).map((e=>{const n=ce.detect(e.versionRegexes,t);return{current:e.name,version:n}})))(r,e))).fold(xe,Ee),i=((e,t)=>ue(e,t).map((e=>{const n=ce.detect(e.versionRegexes,t);return{current:e.name,version:n}})))(o,e).fold(Me,Pe),l=((e,t,n,r)=>{const o=e.isiOS()&&!0===/ipad/i.test(n),s=e.isiOS()&&!o,i=e.isiOS()||e.isAndroid(),l=i||r("(pointer:coarse)"),a=o||!s&&i&&r("(min-device-width:768px)"),c=s||i&&!a,u=t.isSafari()&&e.isiOS()&&!1===/safari/i.test(n),d=!c&&!a&&!u;return{isiPad:p(o),isiPhone:p(s),isTablet:p(a),isPhone:p(c),isTouch:p(l),isAndroid:e.isAndroid,isiOS:e.isiOS,isWebView:p(u),isDesktop:p(d)}})(i,s,e,n);return{browser:s,os:i,deviceType:l}}),Ne=e=>window.matchMedia(e).matches;let He=(e=>{let t,n=!1;return(...r)=>(n||(n=!0,t=e.apply(null,r)),t)})((()=>De(navigator.userAgent,y.from(navigator.userAgentData),Ne)));const Ve=(e,t)=>({left:e,top:t,translate:(n,r)=>Ve(e+n,t+r)}),We=Ve,qe=e=>{const t=void 0===e?window:e;return He().browser.isFirefox()?y.none():y.from(t.visualViewport)},Be=(e,t,n,r)=>({x:e,y:t,width:n,height:r,right:e+n,bottom:t+r}),Ie=e=>{const t=void 0===e?window:e,n=t.document,r=(e=>{const t=void 0!==e?e.dom:document,n=t.body.scrollLeft||t.documentElement.scrollLeft,r=t.body.scrollTop||t.documentElement.scrollTop;return We(n,r)})(j(n));return qe(t).fold((()=>{const e=t.document.documentElement,n=e.clientWidth,o=e.clientHeight;return Be(r.left,r.top,n,o)}),(e=>Be(Math.max(e.pageLeft,r.left),Math.max(e.pageTop,r.top),e.width,e.height)))},je=(e,t,n)=>qe(n).map((n=>{const r=e=>t(re(e));return n.addEventListener(e,r),{unbind:()=>n.removeEventListener(e,r)}})).getOrThunk((()=>({unbind:g})));var _e=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),ze=tinymce.util.Tools.resolve("tinymce.Env");const Ke=(e,t)=>{e.dispatch("FullscreenStateChanged",{state:t}),e.dispatch("ResizeEditor")},$e=("fullscreen_native",e=>e.options.get("fullscreen_native"));const Ue=e=>{return e.dom===(void 0!==(t=z(e).dom).fullscreenElement?t.fullscreenElement:void 0!==t.msFullscreenElement?t.msFullscreenElement:void 0!==t.webkitFullscreenElement?t.webkitFullscreenElement:null);var t},Xe=(e,t,n)=>((e,t,n)=>E(((e,t)=>{const n=m(t)?t:v;let r=e.dom;const o=[];for(;null!==r.parentNode&&void 0!==r.parentNode;){const e=r.parentNode,t=j(e);if(o.push(t),!0===n(t))break;r=e}return o})(e,n),t))(e,(e=>_(e,t)),n),Ye=(e,t)=>((e,n)=>{return E((e=>y.from(e.dom.parentNode).map(j))(r=e).map(K).map((e=>E(e,(e=>{return t=e,!(r.dom===t.dom);var t})))).getOr([]),(e=>_(e,t)));var r})(e),Ge="data-ephox-mobile-fullscreen-style",Je="position:absolute!important;",Qe="top:0!important;left:0!important;margin:0!important;padding:0!important;width:100%!important;height:100%!important;overflow:visible!important;",Ze=ze.os.isAndroid(),et=(e,t,n)=>{const r=t=>n=>{const r=V(n,"style"),o=void 0===r?"no-styles":r.trim();o!==t&&(((e,t,n)=>{((e,t,n)=>{if(!(s(n)||c(n)||h(n)))throw console.error("Invalid call to Attribute.set. Key ",t,":: Value ",n,":: Element ",e),new Error("Attribute value was not simple");e.setAttribute(t,n+"")})(e.dom,t,n)})(n,Ge,o),ee(n,e.parseStyle(t)))},o=Xe(t,"*"),i=(e=>{const t=[];for(let n=0,r=e.length;nYe(e,"*:not(.tox-silver-sink)")))),a=(e=>{const t=te(e,"background-color");return void 0!==t&&""!==t?"background-color:"+t+"!important":"background-color:rgb(255,255,255)!important;"})(n);x(i,r("display:none!important;")),x(o,r(Je+Qe+a)),r((!0===Ze?"":Je)+Qe+a)(t)},tt=_e.DOM,nt=qe().fold((()=>({bind:g,unbind:g})),(e=>{const t=(()=>{const e=O(g);return{...e,on:t=>e.get().each(t)}})(),n=k(),r=k(),o=((e,t)=>{let n=null;return{cancel:()=>{a(n)||(clearTimeout(n),n=null)},throttle:(...t)=>{a(n)&&(n=setTimeout((()=>{n=null,e.apply(null,t)}),50))}}})((()=>{document.body.scrollTop=0,document.documentElement.scrollTop=0,window.requestAnimationFrame((()=>{t.on((t=>ee(t,{top:e.offsetTop+"px",left:e.offsetLeft+"px",height:e.height+"px",width:e.width+"px"})))}))}));return{bind:e=>{t.set(e),o.throttle(),n.set(je("resize",o.throttle)),r.set(je("scroll",o.throttle))},unbind:()=>{t.on((()=>{n.clear(),r.clear()})),t.clear()}}})),rt=(e,t)=>{const n=document.body,r=document.documentElement,o=e.getContainer(),s=j(o),i=(l=s,y.from(l.dom.nextSibling).map(j)).filter((e=>(e=>P(e)&&L(e.dom))(e)&&((e,t)=>(e=>void 0!==e.dom.classList)(e)&&e.dom.classList.contains("tox-silver-sink"))(e)));var l;const a=(e=>{const t=j(e.getElement());return Y(t).map(G).getOrThunk((()=>(e=>{const t=e.dom.body;if(null==t)throw new Error("Body is not available yet");return j(t)})(z(t))))})(e),c=t.get(),u=j(e.getBody()),d=ze.deviceType.isTouch(),m=o.style,h=e.iframeElement,g=null==h?void 0:h.style,p=e=>{e(n,"tox-fullscreen"),e(r,"tox-fullscreen"),e(o,"tox-fullscreen"),Y(s).map((e=>G(e).dom)).each((t=>{e(t,"tox-fullscreen"),e(t,"tox-shadowhost")}))},f=()=>{d&&(e=>{const t=((e,t)=>{const n=document;return 1!==(r=n).nodeType&&9!==r.nodeType&&11!==r.nodeType||0===r.childElementCount?[]:S(n.querySelectorAll(e),j);var r})("["+Ge+"]");x(t,(t=>{const n=V(t,Ge);n&&"no-styles"!==n?ee(t,e.parseStyle(n)):W(t,"style"),W(t,Ge)}))})(e.dom),p(tt.removeClass),nt.unbind(),y.from(t.get()).each((e=>e.fullscreenChangeHandler.unbind()))};if(c)c.fullscreenChangeHandler.unbind(),$e(e)&&Ue(a)&&(e=>{const t=e.dom;t.exitFullscreen?t.exitFullscreen():t.msExitFullscreen?t.msExitFullscreen():t.webkitCancelFullScreen&&t.webkitCancelFullScreen()})(z(a)),g.width=c.iframeWidth,g.height=c.iframeHeight,m.width=c.containerWidth,m.height=c.containerHeight,m.top=c.containerTop,m.left=c.containerLeft,w=i,b=c.sinkCssPosition,E=(e,t)=>{Z(e,"position",t)},w.isSome()&&b.isSome()?y.some(E(w.getOrDie(),b.getOrDie())):y.none(),f(),v=c.scrollPos,window.scrollTo(v.x,v.y),t.set(null),Ke(e,!1),e.off("remove",f);else{const n=ie(z(a),void 0!==document.fullscreenElement?"fullscreenchange":void 0!==document.msFullscreenElement?"MSFullscreenChange":void 0!==document.webkitFullscreenElement?"webkitfullscreenchange":"fullscreenchange",(n=>{$e(e)&&(Ue(a)||null===t.get()||rt(e,t))})),r={scrollPos:Ie(window),containerWidth:m.width,containerHeight:m.height,containerTop:m.top,containerLeft:m.left,iframeWidth:g.width,iframeHeight:g.height,fullscreenChangeHandler:n,sinkCssPosition:i.map((e=>te(e,"position")))};d&&et(e.dom,s,u),g.width=g.height="100%",m.width=m.height="",p(tt.addClass),i.each((e=>{Z(e,"position","fixed")})),nt.bind(s),e.on("remove",f),t.set(r),$e(e)&&(e=>{const t=e.dom;t.requestFullscreen?t.requestFullscreen():t.msRequestFullscreen?t.msRequestFullscreen():t.webkitRequestFullScreen&&t.webkitRequestFullScreen()})(a),Ke(e,!0)}var v,w,b,E};var ot=tinymce.util.Tools.resolve("tinymce.util.VK");const st=(e,t)=>n=>{n.setActive(null!==t.get());const r=e=>n.setActive(e.state);return e.on("FullscreenStateChanged",r),()=>e.off("FullscreenStateChanged",r)};t.add("fullscreen",(t=>{const n=e(null);return t.inline||((e=>{(0,e.options.register)("fullscreen_native",{processor:"boolean",default:!1})})(t),((e,t)=>{e.addCommand("mceFullScreen",(()=>{rt(e,t)}))})(t,n),((e,t)=>{const n=()=>e.execCommand("mceFullScreen");e.ui.registry.addToggleMenuItem("fullscreen",{text:"Fullscreen",icon:"fullscreen",shortcut:"Meta+Shift+F",onAction:n,onSetup:st(e,t)}),e.ui.registry.addToggleButton("fullscreen",{tooltip:"Fullscreen",icon:"fullscreen",onAction:n,onSetup:st(e,t),shortcut:"Meta+Shift+F"})})(t,n),((e,t)=>{e.on("init",(()=>{e.on("keydown",(e=>{e.keyCode!==ot.TAB||e.metaKey||e.ctrlKey||!t.get()||e.preventDefault()}))}))})(t,n),t.addShortcut("Meta+Shift+F","","mceFullScreen")),(e=>({isFullscreen:()=>null!==e.get()}))(n)}))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/ar.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/ar.js new file mode 100644 index 0000000..e2cf02f --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/ar.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.ar', +'

بدء التنقل بواسطة لوحة المفاتيح

\n' + + '\n' + + '
\n' + + '
التركيز على شريط القوائم
\n' + + '
نظاما التشغيل Windows أو Linux: Alt + F9
\n' + + '
نظام التشغيل macOS: ⌥F9
\n' + + '
التركيز على شريط الأدوات
\n' + + '
نظاما التشغيل Windows أو Linux: Alt + F10
\n' + + '
نظام التشغيل macOS: ⌥F10
\n' + + '
التركيز على التذييل
\n' + + '
نظاما التشغيل Windows أو Linux: Alt + F11
\n' + + '
نظام التشغيل macOS: ⌥F11
\n' + + '
تركيز الإشعارات
\n' + + '
نظاما التشغيل Windows أو Linux: Alt + F12
\n' + + '
نظام التشغيل macOS: ⌥F12
\n' + + '
التركيز على شريط أدوات السياق
\n' + + '
أنظمة التشغيل Windows أو Linux أو macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

سيبدأ التنقل عند عنصر واجهة المستخدم الأول، والذي سيتم تمييزه أو تسطيره في حالة العنصر الأول في\n' + + ' مسار عنصر التذييل.

\n' + + '\n' + + '

التنقل بين أقسام واجهة المستخدم

\n' + + '\n' + + '

للانتقال من أحد أقسام واجهة المستخدم إلى القسم التالي، اضغط على Tab.

\n' + + '\n' + + '

للانتقال من أحد أقسام واجهة المستخدم إلى القسم السابق، اضغط على Shift+Tab.

\n' + + '\n' + + '

ترتيب علامات Tab لأقسام واجهة المستخدم هذه هو:

\n' + + '\n' + + '
    \n' + + '
  1. شريط القوائم
  2. \n' + + '
  3. كل مجموعة شريط الأدوات
  4. \n' + + '
  5. الشريط الجانبي
  6. \n' + + '
  7. مسار العنصر في التذييل
  8. \n' + + '
  9. زر تبديل عدد الكلمات في التذييل
  10. \n' + + '
  11. رابط إدراج العلامة التجارية في التذييل
  12. \n' + + '
  13. مؤشر تغيير حجم المحرر في التذييل
  14. \n' + + '
\n' + + '\n' + + '

إذا لم يكن قسم واجهة المستخدم موجودًا، فسيتم تخطيه.

\n' + + '\n' + + '

إذا كان التذييل يحتوي على التركيز على ‏‫التنقل بواسطة لوحة المفاتيح، ولا يوجد شريط جانبي مرئي، فإن الضغط على Shift+Tab\n' + + ' ينقل التركيز إلى مجموعة شريط الأدوات الأولى، وليس الأخيرة.

\n' + + '\n' + + '

التنقل بين أقسام واجهة المستخدم

\n' + + '\n' + + '

للانتقال من أحد عناصر واجهة المستخدم إلى العنصر التالي، اضغط على مفتاح السهم المناسب.

\n' + + '\n' + + '

مفتاحا السهمين اليسار‎ واليمين‎

\n' + + '\n' + + '
    \n' + + '
  • التنقل بين القوائم في شريط القوائم.
  • \n' + + '
  • فتح قائمة فرعية في القائمة.
  • \n' + + '
  • التنقل بين الأزرار في مجموعة شريط الأدوات.
  • \n' + + '
  • التنقل بين العناصر في مسار عنصر التذييل.
  • \n' + + '
\n' + + '\n' + + '

مفتاحا السهمين لأسفل‎ ولأعلى‎

\n' + + '\n' + + '
    \n' + + '
  • التنقل بين عناصر القائمة في القائمة.
  • \n' + + '
  • التنقل بين العناصر في قائمة شريط الأدوات المنبثقة.
  • \n' + + '
\n' + + '\n' + + '

دورة مفاتيح الأسهم‎ داخل قسم واجهة المستخدم التي تم التركيز عليها.

\n' + + '\n' + + '

لإغلاق قائمة مفتوحة أو قائمة فرعية مفتوحة أو قائمة منبثقة مفتوحة، اضغط على مفتاح Esc.

\n' + + '\n' + + '

إذا كان التركيز الحالي على "الجزء العلوي" من قسم معين لواجهة المستخدم، فإن الضغط على مفتاح Esc يؤدي أيضًا إلى الخروج\n' + + ' من التنقل بواسطة لوحة المفاتيح بالكامل.

\n' + + '\n' + + '

تنفيذ عنصر قائمة أو زر شريط أدوات

\n' + + '\n' + + '

عندما يتم تمييز عنصر القائمة المطلوب أو زر شريط الأدوات، اضغط على زر Return، أو Enter،\n' + + ' أو مفتاح المسافة لتنفيذ العنصر.

\n' + + '\n' + + '

التنقل في مربعات الحوار غير المبوبة

\n' + + '\n' + + '

في مربعات الحوار غير المبوبة، يتم التركيز على المكون التفاعلي الأول عند فتح مربع الحوار.

\n' + + '\n' + + '

التنقل بين مكونات الحوار التفاعلي بالضغط على زر Tab أو Shift+Tab.

\n' + + '\n' + + '

التنقل في مربعات الحوار المبوبة

\n' + + '\n' + + '

في مربعات الحوار المبوبة، يتم التركيز على الزر الأول في قائمة علامات التبويب عند فتح مربع الحوار.

\n' + + '\n' + + '

التنقل بين المكونات التفاعلية لعلامة التبويب لمربع الحوار هذه بالضغط على زر Tab أو\n' + + ' Shift+Tab.

\n' + + '\n' + + '

التبديل إلى علامة تبويب أخرى لمربع الحوار من خلال التركيز على قائمة علامة التبويب ثم الضغط على زر السهم المناسب\n' + + ' مفتاح للتنقل بين علامات التبويب المتاحة.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/bg_BG.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/bg_BG.js new file mode 100644 index 0000000..09eacf3 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/bg_BG.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.bg_BG', +'

Начало на навигацията с клавиатурата

\n' + + '\n' + + '
\n' + + '
Фокусиране върху лентата с менюта
\n' + + '
Windows или Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Фокусиране върху лентата с инструменти
\n' + + '
Windows или Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Фокусиране върху долния колонтитул
\n' + + '
Windows или Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Фокусиране на известието
\n' + + '
Windows или Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Фокусиране върху контекстуалната лента с инструменти
\n' + + '
Windows, Linux или macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

Навигацията ще започне с първия елемент на ПИ, който ще бъде маркиран или подчертан в случая на първия елемент в\n' + + ' пътя до елемента в долния колонтитул.

\n' + + '\n' + + '

Навигиране между раздели на ПИ

\n' + + '\n' + + '

За да преминете от един раздел на ПИ към следващия, натиснете Tab.

\n' + + '\n' + + '

За да преминете от един раздел на ПИ към предишния, натиснете Shift+Tab.

\n' + + '\n' + + '

Редът за обхождане с табулация на тези раздели на ПИ е:

\n' + + '\n' + + '
    \n' + + '
  1. Лентата с менюта
  2. \n' + + '
  3. Всяка група на лентата с инструменти
  4. \n' + + '
  5. Страничната лента
  6. \n' + + '
  7. Пътят до елемента в долния колонтитул
  8. \n' + + '
  9. Бутонът за превключване на броя на думите в долния колонтитул
  10. \n' + + '
  11. Връзката за търговска марка в долния колонтитул
  12. \n' + + '
  13. Манипулаторът за преоразмеряване на редактора в долния колонтитул
  14. \n' + + '
\n' + + '\n' + + '

Ако някой раздел на ПИ липсва, той се пропуска.

\n' + + '\n' + + '

Ако долният колонтитул има фокус за навигация с клавиатурата и няма странична лента, натискането на Shift+Tab\n' + + ' премества фокуса към първата група на лентата с инструменти, а не към последната.

\n' + + '\n' + + '

Навигиране в разделите на ПИ

\n' + + '\n' + + '

За да преминете от един елемент на ПИ към следващия, натиснете съответния клавиш със стрелка.

\n' + + '\n' + + '

С клавишите със стрелка наляво и надясно

\n' + + '\n' + + '
    \n' + + '
  • се придвижвате между менютата в лентата с менюто;
  • \n' + + '
  • отваряте подменю в меню;
  • \n' + + '
  • се придвижвате между бутоните в група на лентата с инструменти;
  • \n' + + '
  • се придвижвате между елементи в пътя до елемент в долния колонтитул.
  • \n' + + '
\n' + + '\n' + + '

С клавишите със стрелка надолу и нагоре

\n' + + '\n' + + '
    \n' + + '
  • се придвижвате между елементите от менюто в дадено меню;
  • \n' + + '
  • се придвижвате между елементите в изскачащо меню на лентата с инструменти.
  • \n' + + '
\n' + + '\n' + + '

Клавишите със стрелки се придвижват в рамките на фокусирания раздел на ПИ.

\n' + + '\n' + + '

За да затворите отворено меню, подменю или изскачащо меню, натиснете клавиша Esc.

\n' + + '\n' + + '

Ако текущият фокус е върху „горната част“ на конкретен раздел на ПИ, натискането на клавиша Esc също излиза\n' + + ' напълно от навигацията с клавиатурата.

\n' + + '\n' + + '

Изпълнение на елемент от менюто или бутон от лентата с инструменти

\n' + + '\n' + + '

Когато желаният елемент от менюто или бутон от лентата с инструменти е маркиран, натиснете Return, Enter\n' + + ' или клавиша за интервал, за да изпълните елемента.

\n' + + '\n' + + '

Навигиране в диалогови прозорци без раздели

\n' + + '\n' + + '

В диалоговите прозорци без раздели първият интерактивен компонент се фокусира, когато се отвори диалоговият прозорец.

\n' + + '\n' + + '

Навигирайте между интерактивните компоненти на диалоговия прозорец, като натиснете Tab или Shift+Tab.

\n' + + '\n' + + '

Навигиране в диалогови прозорци с раздели

\n' + + '\n' + + '

В диалоговите прозорци с раздели първият бутон в менюто с раздели се фокусира, когато се отвори диалоговият прозорец.

\n' + + '\n' + + '

Навигирайте между интерактивните компоненти на този диалогов раздел, като натиснете Tab или\n' + + ' Shift+Tab.

\n' + + '\n' + + '

Превключете към друг диалогов раздел, като фокусирате върху менюто с раздели и след това натиснете съответния клавиш със стрелка,\n' + + ' за да преминете през наличните раздели.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/ca.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/ca.js new file mode 100644 index 0000000..996e29c --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/ca.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.ca', +'

Inici de la navegació amb el teclat

\n' + + '\n' + + '
\n' + + '
Enfocar la barra de menús
\n' + + '
Windows o Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + "
Enfocar la barra d'eines
\n" + + '
Windows o Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Enfocar el peu de pàgina
\n' + + '
Windows o Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Enfocar la notificació
\n' + + '
Windows o Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + "
Enfocar una barra d'eines contextual
\n" + + '
Windows, Linux o macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + "

La navegació començarà en el primer element de la interfície d'usuari, que es ressaltarà o subratllarà per al primer element a\n" + + " la ruta de l'element de peu de pàgina.

\n" + + '\n' + + "

Navegació entre seccions de la interfície d'usuari

\n" + + '\n' + + "

Per desplaçar-vos des d'una secció de la interfície d'usuari a la següent, premeu la tecla Tab.

\n" + + '\n' + + "

Per desplaçar-vos des d'una secció de la interfície d'usuari a l'anterior, premeu les tecles Maj+Tab.

\n" + + '\n' + + "

L'ordre en prémer la tecla Tab d'aquestes secciones de la interfície d'usuari és:

\n" + + '\n' + + '
    \n' + + '
  1. Barra de menús
  2. \n' + + "
  3. Cada grup de la barra d'eines
  4. \n" + + '
  5. Barra lateral
  6. \n' + + "
  7. Ruta de l'element del peu de pàgina
  8. \n" + + '
  9. Botó de commutació de recompte de paraules al peu de pàgina
  10. \n' + + '
  11. Enllaç de marca del peu de pàgina
  12. \n' + + "
  13. Control de canvi de mida de l'editor al peu de pàgina
  14. \n" + + '
\n' + + '\n' + + "

Si no hi ha una secció de la interfície d'usuari, s'ometrà.

\n" + + '\n' + + '

Si el peu de pàgina té el focus de navegació del teclat i no hi ha cap barra lateral visible, en prémer Maj+Tab\n' + + " el focus es mou al primer grup de la barra d'eines, no l'últim.

\n" + + '\n' + + "

Navegació dins de les seccions de la interfície d'usuari

\n" + + '\n' + + "

Per desplaçar-vos des d'un element de la interfície d'usuari al següent, premeu la tecla de Fletxa adequada.

\n" + + '\n' + + '

Les tecles de fletxa Esquerra i Dreta

\n' + + '\n' + + '
    \n' + + '
  • us permeten desplaçar-vos entre menús de la barra de menús.
  • \n' + + '
  • obren un submenú en un menú.
  • \n' + + "
  • us permeten desplaçar-vos entre botons d'un grup de la barra d'eines.
  • \n" + + "
  • us permeten desplaçar-vos entre elements de la ruta d'elements del peu de pàgina.
  • \n" + + '
\n' + + '\n' + + '

Les tecles de fletxa Avall i Amunt

\n' + + '\n' + + '
    \n' + + "
  • us permeten desplaçar-vos entre elements de menú d'un menú.
  • \n" + + "
  • us permeten desplaçar-vos entre elements d'un menú emergent de la barra d'eines.
  • \n" + + '
\n' + + '\n' + + "

Les tecles de Fletxa us permeten desplaçar-vos dins de la secció de la interfície d'usuari que té el focus.

\n" + + '\n' + + '

Per tancar un menú, un submenú o un menú emergent oberts, premeu la tecla Esc.

\n' + + '\n' + + "

Si el focus actual es troba a la ‘part superior’ d'una secció específica de la interfície d'usuari, en prémer la tecla Esc també es tanca\n" + + ' completament la navegació amb el teclat.

\n' + + '\n' + + "

Execució d'un element de menú o d'un botó de la barra d'eines

\n" + + '\n' + + "

Quan l'element del menú o el botó de la barra d'eines que desitgeu estigui ressaltat, premeu Retorn, Intro\n" + + " o la barra d'espai per executar l'element.

\n" + + '\n' + + '

Navegació per quadres de diàleg sense pestanyes

\n' + + '\n' + + "

En els quadres de diàleg sense pestanyes, el primer component interactiu pren el focus quan s'obre el quadre diàleg.

\n" + + '\n' + + '

Premeu la tecla Tab o les tecles Maj+Tab per desplaçar-vos entre components interactius del quadre de diàleg.

\n' + + '\n' + + '

Navegació per quadres de diàleg amb pestanyes

\n' + + '\n' + + "

En els quadres de diàleg amb pestanyes, el primer botó del menú de la pestanya pren el focus quan s'obre el quadre diàleg.

\n" + + '\n' + + "

Per desplaçar-vos entre components interactius d'aquest quadre de diàleg, premeu la tecla Tab o\n" + + ' les tecles Maj+Tab.

\n' + + '\n' + + "

Canvieu a la pestanya d'un altre quadre de diàleg, tot enfocant el menú de la pestanya, i després premeu la tecla Fletxa adequada\n" + + ' per canviar entre les pestanyes disponibles.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/cs.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/cs.js new file mode 100644 index 0000000..4a5a902 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/cs.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.cs', +'

Začínáme navigovat pomocí klávesnice

\n' + + '\n' + + '
\n' + + '
Přejít na řádek nabídek
\n' + + '
Windows nebo Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Přejít na panel nástrojů
\n' + + '
Windows nebo Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Přejít na zápatí
\n' + + '
Windows nebo Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Přejít na oznámení
\n' + + '
Windows nebo Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Přejít na kontextový panel nástrojů
\n' + + '
Windows, Linux nebo macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

Navigace začne u první položky uživatelského rozhraní, která bude zvýrazněna nebo v případě první položky\n' + + ' cesty k prvku zápatí podtržena.

\n' + + '\n' + + '

Navigace mezi oddíly uživatelského rozhraní

\n' + + '\n' + + '

Stisknutím klávesy Tab se posunete z jednoho oddílu uživatelského rozhraní na další.

\n' + + '\n' + + '

Stisknutím kláves Shift+Tab se posunete z jednoho oddílu uživatelského rozhraní na předchozí.

\n' + + '\n' + + '

Pořadí přepínání mezi oddíly uživatelského rozhraní pomocí klávesy Tab:

\n' + + '\n' + + '
    \n' + + '
  1. Řádek nabídek
  2. \n' + + '
  3. Každá skupina panelu nástrojů
  4. \n' + + '
  5. Boční panel
  6. \n' + + '
  7. Cesta k prvku v zápatí.
  8. \n' + + '
  9. Tlačítko přepínače počtu slov v zápatí
  10. \n' + + '
  11. Odkaz na informace o značce v zápatí
  12. \n' + + '
  13. Úchyt pro změnu velikosti editoru v zápatí
  14. \n' + + '
\n' + + '\n' + + '

Pokud nějaký oddíl uživatelského rozhraní není přítomen, je přeskočen.

\n' + + '\n' + + '

Pokud je zápatí vybrané pro navigaci pomocí klávesnice a není zobrazen žádný boční panel, stisknutím kláves Shift+Tab\n' + + ' přejdete na první skupinu panelu nástrojů, nikoli na poslední.

\n' + + '\n' + + '

Navigace v rámci oddílů uživatelského rozhraní

\n' + + '\n' + + '

Chcete-li se přesunout z jednoho prvku uživatelského rozhraní na další, stiskněte příslušnou klávesu s šipkou.

\n' + + '\n' + + '

Klávesy s šipkou vlevovpravo

\n' + + '\n' + + '
    \n' + + '
  • umožňují přesun mezi nabídkami na řádku nabídek;
  • \n' + + '
  • otevírají podnabídku nabídky;
  • \n' + + '
  • umožňují přesun mezi tlačítky ve skupině panelu nástrojů;
  • \n' + + '
  • umožňují přesun mezi položkami cesty prvku v zápatí.
  • \n' + + '
\n' + + '\n' + + '

Klávesy se šipkou dolůnahoru

\n' + + '\n' + + '
    \n' + + '
  • umožňují přesun mezi položkami nabídky;
  • \n' + + '
  • umožňují přesun mezi položkami místní nabídky panelu nástrojů.
  • \n' + + '
\n' + + '\n' + + '

Šipky provádí přepínání v rámci vybraného oddílu uživatelského rozhraní.

\n' + + '\n' + + '

Chcete-li zavřít otevřenou nabídku, podnabídku nebo místní nabídku, stiskněte klávesu Esc.

\n' + + '\n' + + '

Pokud je aktuálně vybrána horní část oddílu uživatelského rozhraní, stisknutím klávesy Esc zcela ukončíte také\n' + + ' navigaci pomocí klávesnice.

\n' + + '\n' + + '

Provedení příkazu položky nabídky nebo tlačítka panelu nástrojů

\n' + + '\n' + + '

Pokud je zvýrazněna požadovaná položka nabídky nebo tlačítko panelu nástrojů, stisknutím klávesy Return, Enter\n' + + ' nebo mezerníku provedete příslušný příkaz.

\n' + + '\n' + + '

Navigace v dialogových oknech bez záložek

\n' + + '\n' + + '

Při otevření dialogových oken bez záložek přejdete na první interaktivní komponentu.

\n' + + '\n' + + '

Přecházet mezi interaktivními komponentami dialogového okna můžete stisknutím klávesy Tab nebo kombinace Shift+Tab.

\n' + + '\n' + + '

Navigace v dialogových oknech se záložkami

\n' + + '\n' + + '

Při otevření dialogových oken se záložkami přejdete na první tlačítko v nabídce záložek.

\n' + + '\n' + + '

Přecházet mezi interaktivními komponentami této záložky dialogového okna můžete stisknutím klávesy Tab nebo\n' + + ' kombinace Shift+Tab.

\n' + + '\n' + + '

Chcete-li přepnout na další záložku dialogového okna, přejděte na nabídku záložek a poté můžete stisknutím požadované šipky\n' + + ' přepínat mezi dostupnými záložkami.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/da.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/da.js new file mode 100644 index 0000000..4d1e1d4 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/da.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.da', +'

Start tastaturnavigation

\n' + + '\n' + + '
\n' + + '
Fokuser på menulinjen
\n' + + '
Windows eller Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Fokuser på værktøjslinjen
\n' + + '
Windows eller Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Fokuser på sidefoden
\n' + + '
Windows eller Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Fokuser på meddelelsen
\n' + + '
Windows eller Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Fokuser på kontekstuel værktøjslinje
\n' + + '
Windows, Linux eller macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

Navigationen starter ved det første UI-element, som fremhæves eller understreges hvad angår det første element i\n' + + ' sidefodens sti til elementet.

\n' + + '\n' + + '

Naviger mellem UI-sektioner

\n' + + '\n' + + '

Gå fra én UI-sektion til den næste ved at trykke på Tab.

\n' + + '\n' + + '

Gå fra én UI-sektion til den forrige ved at trykke på Shift+Tab.

\n' + + '\n' + + '

Tab-rækkefølgen af disse UI-sektioner er:

\n' + + '\n' + + '
    \n' + + '
  1. Menulinje
  2. \n' + + '
  3. Hver værktøjsgruppe
  4. \n' + + '
  5. Sidepanel
  6. \n' + + '
  7. Sti til elementet i sidefoden
  8. \n' + + '
  9. Til/fra-knap for ordoptælling i sidefoden
  10. \n' + + '
  11. Brandinglink i sidefoden
  12. \n' + + '
  13. Tilpasningshåndtag for editor i sidefoden
  14. \n' + + '
\n' + + '\n' + + '

Hvis en UI-sektion ikke er til stede, springes den over.

\n' + + '\n' + + '

Hvis sidefoden har fokus til tastaturnavigation, og der ikke er noget synligt sidepanel, kan der trykkes på Shift+Tab\n' + + ' for at flytte fokus til den første værktøjsgruppe, ikke den sidste.

\n' + + '\n' + + '

Naviger inden for UI-sektioner

\n' + + '\n' + + '

Gå fra ét UI-element til det næste ved at trykke på den relevante piletast.

\n' + + '\n' + + '

Venstre og højre piletast

\n' + + '\n' + + '
    \n' + + '
  • flytter mellem menuerne i menulinjen.
  • \n' + + '
  • åbner en undermenu i en menu.
  • \n' + + '
  • flytter mellem knapperne i en værktøjsgruppe.
  • \n' + + '
  • flytter mellem elementer i sidefodens sti til elementet.
  • \n' + + '
\n' + + '\n' + + '

Pil ned og op

\n' + + '\n' + + '
    \n' + + '
  • flytter mellem menupunkterne i en menu.
  • \n' + + '
  • flytter mellem punkterne i en genvejsmenu i værktøjslinjen.
  • \n' + + '
\n' + + '\n' + + '

Piletasterne kører rundt inden for UI-sektionen, der fokuseres på.

\n' + + '\n' + + '

For at lukke en åben menu, en åben undermenu eller en åben genvejsmenu trykkes der på Esc-tasten.

\n' + + '\n' + + "

Hvis det aktuelle fokus er i 'toppen' af en bestemt UI-sektion, vil tryk på Esc-tasten også afslutte\n" + + ' tastaturnavigationen helt.

\n' + + '\n' + + '

Udfør et menupunkt eller en værktøjslinjeknap

\n' + + '\n' + + '

Når det ønskede menupunkt eller den ønskede værktøjslinjeknap er fremhævet, trykkes der på Retur, Enter\n' + + ' eller mellemrumstasten for at udføre elementet.

\n' + + '\n' + + '

Naviger i ikke-faneopdelte dialogbokse

\n' + + '\n' + + '

I ikke-faneopdelte dialogbokse får den første interaktive komponent fokus, når dialogboksen åbnes.

\n' + + '\n' + + '

Naviger mellem interaktive dialogbokskomponenter ved at trykke på Tab eller Shift+Tab.

\n' + + '\n' + + '

Naviger i faneopdelte dialogbokse

\n' + + '\n' + + '

I faneopdelte dialogbokse får den første knap i fanemenuen fokus, når dialogboksen åbnes.

\n' + + '\n' + + '

Naviger mellem interaktive komponenter i denne dialogboksfane ved at trykke på Tab eller\n' + + ' Shift+Tab.

\n' + + '\n' + + '

Skift til en anden dialogboksfane ved at fokusere på fanemenuen og derefter trykke på den relevante piletast\n' + + ' for at køre igennem de tilgængelige faner.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/de.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/de.js new file mode 100644 index 0000000..b8711ed --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/de.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.de', +'

Grundlagen der Tastaturnavigation

\n' + + '\n' + + '
\n' + + '
Fokus auf Menüleiste
\n' + + '
Windows oder Linux: ALT+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Fokus auf Symbolleiste
\n' + + '
Windows oder Linux: ALT+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Fokus auf Fußzeile
\n' + + '
Windows oder Linux: ALT+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Benachrichtigung fokussieren
\n' + + '
Windows oder Linux: ALT+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Fokus auf kontextbezogene Symbolleiste
\n' + + '
Windows, Linux oder macOS: STRG+F9
\n' + + '
\n' + + '\n' + + '

Die Navigation beginnt beim ersten Benutzeroberflächenelement, welches hervorgehoben ist. Falls sich das erste Element im Pfad der Fußzeile befindet,\n' + + ' ist es unterstrichen.

\n' + + '\n' + + '

Zwischen Abschnitten der Benutzeroberfläche navigieren

\n' + + '\n' + + '

Um von einem Abschnitt der Benutzeroberfläche zum nächsten zu wechseln, drücken Sie TAB.

\n' + + '\n' + + '

Um von einem Abschnitt der Benutzeroberfläche zum vorherigen zu wechseln, drücken Sie UMSCHALT+TAB.

\n' + + '\n' + + '

Die Abschnitte der Benutzeroberfläche haben folgende TAB-Reihenfolge:

\n' + + '\n' + + '
    \n' + + '
  1. Menüleiste
  2. \n' + + '
  3. Einzelne Gruppen der Symbolleiste
  4. \n' + + '
  5. Randleiste
  6. \n' + + '
  7. Elementpfad in der Fußzeile
  8. \n' + + '
  9. Umschaltfläche „Wörter zählen“ in der Fußzeile
  10. \n' + + '
  11. Branding-Link in der Fußzeile
  12. \n' + + '
  13. Editor-Ziehpunkt zur Größenänderung in der Fußzeile
  14. \n' + + '
\n' + + '\n' + + '

Falls ein Abschnitt der Benutzeroberflächen nicht vorhanden ist, wird er übersprungen.

\n' + + '\n' + + '

Wenn in der Fußzeile die Tastaturnavigation fokussiert ist und keine Randleiste angezeigt wird, wechselt der Fokus durch Drücken von UMSCHALT+TAB\n' + + ' zur ersten Gruppe der Symbolleiste, nicht zur letzten.

\n' + + '\n' + + '

Innerhalb von Abschnitten der Benutzeroberfläche navigieren

\n' + + '\n' + + '

Um von einem Element der Benutzeroberfläche zum nächsten zu wechseln, drücken Sie die entsprechende Pfeiltaste.

\n' + + '\n' + + '

Die Pfeiltasten Links und Rechts

\n' + + '\n' + + '
    \n' + + '
  • wechseln zwischen Menüs in der Menüleiste.
  • \n' + + '
  • öffnen das Untermenü eines Menüs.
  • \n' + + '
  • wechseln zwischen Schaltflächen in einer Gruppe der Symbolleiste.
  • \n' + + '
  • wechseln zwischen Elementen im Elementpfad der Fußzeile.
  • \n' + + '
\n' + + '\n' + + '

Die Pfeiltasten Abwärts und Aufwärts

\n' + + '\n' + + '
    \n' + + '
  • wechseln zwischen Menüelementen in einem Menü.
  • \n' + + '
  • wechseln zwischen Elementen in einem Popupmenü der Symbolleiste.
  • \n' + + '
\n' + + '\n' + + '

Die Pfeiltasten rotieren innerhalb des fokussierten Abschnitts der Benutzeroberfläche.

\n' + + '\n' + + '

Um ein geöffnetes Menü, ein geöffnetes Untermenü oder ein geöffnetes Popupmenü zu schließen, drücken Sie die ESC-Taste.

\n' + + '\n' + + '

Wenn sich der aktuelle Fokus ganz oben in einem bestimmten Abschnitt der Benutzeroberfläche befindet, wird durch Drücken der ESC-Taste auch\n' + + ' die Tastaturnavigation beendet.

\n' + + '\n' + + '

Ein Menüelement oder eine Symbolleistenschaltfläche ausführen

\n' + + '\n' + + '

Wenn das gewünschte Menüelement oder die gewünschte Symbolleistenschaltfläche hervorgehoben ist, drücken Sie Zurück, Eingabe\n' + + ' oder die Leertaste, um das Element auszuführen.

\n' + + '\n' + + '

In Dialogfeldern ohne Registerkarten navigieren

\n' + + '\n' + + '

In Dialogfeldern ohne Registerkarten ist beim Öffnen eines Dialogfelds die erste interaktive Komponente fokussiert.

\n' + + '\n' + + '

Navigieren Sie zwischen den interaktiven Komponenten eines Dialogfelds, indem Sie TAB oder UMSCHALT+TAB drücken.

\n' + + '\n' + + '

In Dialogfeldern mit Registerkarten navigieren

\n' + + '\n' + + '

In Dialogfeldern mit Registerkarten ist beim Öffnen eines Dialogfelds die erste Schaltfläche eines Registerkartenmenüs fokussiert.

\n' + + '\n' + + '

Navigieren Sie zwischen den interaktiven Komponenten auf dieser Registerkarte des Dialogfelds, indem Sie TAB oder\n' + + ' UMSCHALT+TAB drücken.

\n' + + '\n' + + '

Wechseln Sie zu einer anderen Registerkarte des Dialogfelds, indem Sie den Fokus auf das Registerkartenmenü legen und dann die entsprechende Pfeiltaste\n' + + ' drücken, um durch die verfügbaren Registerkarten zu rotieren.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/el.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/el.js new file mode 100644 index 0000000..98afabe --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/el.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.el', +'

Έναρξη πλοήγησης μέσω πληκτρολογίου

\n' + + '\n' + + '
\n' + + '
Εστίαση στη γραμμή μενού
\n' + + '
Windows ή Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Εστίαση στη γραμμή εργαλείων
\n' + + '
Windows ή Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Εστίαση στο υποσέλιδο
\n' + + '
Windows ή Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Εστίαση στην ειδοποίηση
\n' + + '
Windows ή Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Εστίαση σε γραμμή εργαλείων βάσει περιεχομένου
\n' + + '
Windows, Linux ή macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

Η πλοήγηση θα ξεκινήσει από το πρώτο στοιχείο περιβάλλοντος χρήστη, που θα επισημαίνεται ή θα είναι υπογραμμισμένο,\n' + + ' όπως στην περίπτωση της διαδρομής του στοιχείου Υποσέλιδου.

\n' + + '\n' + + '

Πλοήγηση μεταξύ ενοτήτων του περιβάλλοντος χρήστη

\n' + + '\n' + + '

Για να μετακινηθείτε από μια ενότητα περιβάλλοντος χρήστη στην επόμενη, πιέστε το πλήκτρο Tab.

\n' + + '\n' + + '

Για να μετακινηθείτε από μια ενότητα περιβάλλοντος χρήστη στην προηγούμενη, πιέστε τα πλήκτρα Shift+Tab.

\n' + + '\n' + + '

Η σειρά Tab αυτών των ενοτήτων περιβάλλοντος χρήστη είναι η εξής:

\n' + + '\n' + + '
    \n' + + '
  1. Γραμμή μενού
  2. \n' + + '
  3. Κάθε ομάδα γραμμής εργαλείων
  4. \n' + + '
  5. Πλαϊνή γραμμή
  6. \n' + + '
  7. Διαδρομή στοιχείου στο υποσέλιδο
  8. \n' + + '
  9. Κουμπί εναλλαγής μέτρησης λέξεων στο υποσέλιδο
  10. \n' + + '
  11. Σύνδεσμος επωνυμίας στο υποσέλιδο
  12. \n' + + '
  13. Λαβή αλλαγής μεγέθους προγράμματος επεξεργασίας στο υποσέλιδο
  14. \n' + + '
\n' + + '\n' + + '

Εάν δεν εμφανίζεται ενότητα περιβάλλοντος χρήστη, παραλείπεται.

\n' + + '\n' + + '

Εάν η εστίαση πλοήγησης βρίσκεται στο πληκτρολόγιο και δεν υπάρχει εμφανής πλαϊνή γραμμή, εάν πιέσετε Shift+Tab\n' + + ' η εστίαση μετακινείται στην πρώτη ομάδα γραμμής εργαλείων, όχι στην τελευταία.

\n' + + '\n' + + '

Πλοήγηση εντός των ενοτήτων του περιβάλλοντος χρήστη

\n' + + '\n' + + '

Για να μετακινηθείτε από ένα στοιχείο περιβάλλοντος χρήστη στο επόμενο, πιέστε το αντίστοιχο πλήκτρο βέλους.

\n' + + '\n' + + '

Με τα πλήκτρα αριστερού και δεξιού βέλους

\n' + + '\n' + + '
    \n' + + '
  • γίνεται μετακίνηση μεταξύ των μενού στη γραμμή μενού.
  • \n' + + '
  • ανοίγει ένα υπομενού σε ένα μενού.
  • \n' + + '
  • γίνεται μετακίνηση μεταξύ κουμπιών σε μια ομάδα γραμμής εργαλείων.
  • \n' + + '
  • γίνεται μετακίνηση μεταξύ στοιχείων στη διαδρομή στοιχείου στο υποσέλιδο.
  • \n' + + '
\n' + + '\n' + + '

Με τα πλήκτρα επάνω και κάτω βέλους

\n' + + '\n' + + '
    \n' + + '
  • γίνεται μετακίνηση μεταξύ των στοιχείων μενού σε ένα μενού.
  • \n' + + '
  • γίνεται μετακίνηση μεταξύ των στοιχείων μενού σε ένα αναδυόμενο μενού γραμμής εργαλείων.
  • \n' + + '
\n' + + '\n' + + '

Με τα πλήκτρα βέλους γίνεται κυκλική μετακίνηση εντός της εστιασμένης ενότητας περιβάλλοντος χρήστη.

\n' + + '\n' + + '

Για να κλείσετε ένα ανοιχτό μενού, ένα ανοιχτό υπομενού ή ένα ανοιχτό αναδυόμενο μενού, πιέστε το πλήκτρο Esc.

\n' + + '\n' + + '

Εάν η τρέχουσα εστίαση βρίσκεται στην κορυφή μιας ενότητας περιβάλλοντος χρήστη, πιέζοντας το πλήκτρο Esc,\n' + + ' γίνεται επίσης πλήρης έξοδος από την πλοήγηση μέσω πληκτρολογίου.

\n' + + '\n' + + '

Εκτέλεση ενός στοιχείου μενού ή κουμπιού γραμμής εργαλείων

\n' + + '\n' + + '

Όταν το επιθυμητό στοιχείο μενού ή κουμπί γραμμής εργαλείων είναι επισημασμένο, πιέστε τα πλήκτρα Return, Enter,\n' + + ' ή το πλήκτρο διαστήματος για να εκτελέσετε το στοιχείο.

\n' + + '\n' + + '

Πλοήγηση σε παράθυρα διαλόγου χωρίς καρτέλες

\n' + + '\n' + + '

Σε παράθυρα διαλόγου χωρίς καρτέλες, το πρώτο αλληλεπιδραστικό στοιχείο λαμβάνει την εστίαση όταν ανοίγει το παράθυρο διαλόγου.

\n' + + '\n' + + '

Μπορείτε να πλοηγηθείτε μεταξύ των αλληλεπιδραστικών στοιχείων παραθύρων διαλόγων πιέζοντας τα πλήκτρα Tab ή Shift+Tab.

\n' + + '\n' + + '

Πλοήγηση σε παράθυρα διαλόγου με καρτέλες

\n' + + '\n' + + '

Σε παράθυρα διαλόγου με καρτέλες, το πρώτο κουμπί στο μενού καρτέλας λαμβάνει την εστίαση όταν ανοίγει το παράθυρο διαλόγου.

\n' + + '\n' + + '

Μπορείτε να πλοηγηθείτε μεταξύ των αλληλεπιδραστικών στοιχείων αυτής της καρτέλα διαλόγου πιέζοντας τα πλήκτρα Tab ή\n' + + ' Shift+Tab.

\n' + + '\n' + + '

Μπορείτε να κάνετε εναλλαγή σε άλλη καρτέλα του παραθύρου διαλόγου, μεταφέροντας την εστίαση στο μενού καρτέλας και πιέζοντας το κατάλληλο πλήκτρο βέλους\n' + + ' για να μετακινηθείτε κυκλικά στις διαθέσιμες καρτέλες.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/en.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/en.js new file mode 100644 index 0000000..5dd753e --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/en.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.en', +'

Begin keyboard navigation

\n' + + '\n' + + '
\n' + + '
Focus the Menu bar
\n' + + '
Windows or Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Focus the Toolbar
\n' + + '
Windows or Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Focus the footer
\n' + + '
Windows or Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Focus the notification
\n' + + '
Windows or Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Focus a contextual toolbar
\n' + + '
Windows, Linux or macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

Navigation will start at the first UI item, which will be highlighted, or underlined in the case of the first item in\n' + + ' the Footer element path.

\n' + + '\n' + + '

Navigate between UI sections

\n' + + '\n' + + '

To move from one UI section to the next, press Tab.

\n' + + '\n' + + '

To move from one UI section to the previous, press Shift+Tab.

\n' + + '\n' + + '

The Tab order of these UI sections is:

\n' + + '\n' + + '
    \n' + + '
  1. Menu bar
  2. \n' + + '
  3. Each toolbar group
  4. \n' + + '
  5. Sidebar
  6. \n' + + '
  7. Element path in the footer
  8. \n' + + '
  9. Word count toggle button in the footer
  10. \n' + + '
  11. Branding link in the footer
  12. \n' + + '
  13. Editor resize handle in the footer
  14. \n' + + '
\n' + + '\n' + + '

If a UI section is not present, it is skipped.

\n' + + '\n' + + '

If the footer has keyboard navigation focus, and there is no visible sidebar, pressing Shift+Tab\n' + + ' moves focus to the first toolbar group, not the last.

\n' + + '\n' + + '

Navigate within UI sections

\n' + + '\n' + + '

To move from one UI element to the next, press the appropriate Arrow key.

\n' + + '\n' + + '

The Left and Right arrow keys

\n' + + '\n' + + '
    \n' + + '
  • move between menus in the menu bar.
  • \n' + + '
  • open a sub-menu in a menu.
  • \n' + + '
  • move between buttons in a toolbar group.
  • \n' + + '
  • move between items in the footer’s element path.
  • \n' + + '
\n' + + '\n' + + '

The Down and Up arrow keys

\n' + + '\n' + + '
    \n' + + '
  • move between menu items in a menu.
  • \n' + + '
  • move between items in a toolbar pop-up menu.
  • \n' + + '
\n' + + '\n' + + '

Arrow keys cycle within the focused UI section.

\n' + + '\n' + + '

To close an open menu, an open sub-menu, or an open pop-up menu, press the Esc key.

\n' + + '\n' + + '

If the current focus is at the ‘top’ of a particular UI section, pressing the Esc key also exits\n' + + ' keyboard navigation entirely.

\n' + + '\n' + + '

Execute a menu item or toolbar button

\n' + + '\n' + + '

When the desired menu item or toolbar button is highlighted, press Return, Enter,\n' + + ' or the Space bar to execute the item.

\n' + + '\n' + + '

Navigate non-tabbed dialogs

\n' + + '\n' + + '

In non-tabbed dialogs, the first interactive component takes focus when the dialog opens.

\n' + + '\n' + + '

Navigate between interactive dialog components by pressing Tab or Shift+Tab.

\n' + + '\n' + + '

Navigate tabbed dialogs

\n' + + '\n' + + '

In tabbed dialogs, the first button in the tab menu takes focus when the dialog opens.

\n' + + '\n' + + '

Navigate between interactive components of this dialog tab by pressing Tab or\n' + + ' Shift+Tab.

\n' + + '\n' + + '

Switch to another dialog tab by giving the tab menu focus and then pressing the appropriate Arrow\n' + + ' key to cycle through the available tabs.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/es.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/es.js new file mode 100644 index 0000000..e426c2e --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/es.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.es', +'

Iniciar la navegación con el teclado

\n' + + '\n' + + '
\n' + + '
Enfocar la barra de menús
\n' + + '
Windows o Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Enfocar la barra de herramientas
\n' + + '
Windows o Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Enfocar el pie de página
\n' + + '
Windows o Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Enfocar la notificación
\n' + + '
Windows o Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Enfocar una barra de herramientas contextual
\n' + + '
Windows, Linux o macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

La navegación comenzará por el primer elemento de la interfaz de usuario (IU), de tal manera que se resaltará, o bien se subrayará si se trata del primer elemento de\n' + + ' la ruta de elemento del pie de página.

\n' + + '\n' + + '

Navegar entre las secciones de la IU

\n' + + '\n' + + '

Para pasar de una sección de la IU a la siguiente, pulse la tecla Tab.

\n' + + '\n' + + '

Para pasar de una sección de la IU a la anterior, pulse Mayús+Tab.

\n' + + '\n' + + '

El orden de tabulación de estas secciones de la IU es:

\n' + + '\n' + + '
    \n' + + '
  1. Barra de menús
  2. \n' + + '
  3. Cada grupo de barra de herramientas
  4. \n' + + '
  5. Barra lateral
  6. \n' + + '
  7. Ruta del elemento en el pie de página
  8. \n' + + '
  9. Botón de alternancia de recuento de palabras en el pie de página
  10. \n' + + '
  11. Enlace de personalización de marca en el pie de página
  12. \n' + + '
  13. Controlador de cambio de tamaño en el pie de página
  14. \n' + + '
\n' + + '\n' + + '

Si una sección de la IU no está presente, esta se omite.

\n' + + '\n' + + '

Si el pie de página tiene un enfoque de navegación con el teclado y no hay ninguna barra lateral visible, al pulsar Mayús+Tab,\n' + + ' el enfoque se moverá al primer grupo de barra de herramientas, en lugar de al último.

\n' + + '\n' + + '

Navegar dentro de las secciones de la IU

\n' + + '\n' + + '

Para pasar de un elemento de la IU al siguiente, pulse la tecla de flecha correspondiente.

\n' + + '\n' + + '

Las teclas de flecha izquierda y derecha permiten

\n' + + '\n' + + '
    \n' + + '
  • desplazarse entre los menús de la barra de menús.
  • \n' + + '
  • abrir el submenú de un menú.
  • \n' + + '
  • desplazarse entre los botones de un grupo de barra de herramientas.
  • \n' + + '
  • desplazarse entre los elementos de la ruta de elemento del pie de página.
  • \n' + + '
\n' + + '\n' + + '

Las teclas de flecha abajo y arriba permiten

\n' + + '\n' + + '
    \n' + + '
  • desplazarse entre los elementos de menú de un menú.
  • \n' + + '
  • desplazarse entre los elementos de un menú emergente de una barra de herramientas.
  • \n' + + '
\n' + + '\n' + + '

Las teclas de flecha van cambiando dentro de la sección de la IU enfocada.

\n' + + '\n' + + '

Para cerrar un menú, un submenú o un menú emergente que estén abiertos, pulse la tecla Esc.

\n' + + '\n' + + '

Si el enfoque actual se encuentra en la parte superior de una sección de la IU determinada, al pulsar la tecla Esc saldrá\n' + + ' de la navegación con el teclado por completo.

\n' + + '\n' + + '

Ejecutar un elemento de menú o un botón de barra de herramientas

\n' + + '\n' + + '

Si el elemento de menú o el botón de barra de herramientas deseado está resaltado, pulse la tecla Retorno o Entrar,\n' + + ' o la barra espaciadora para ejecutar el elemento.

\n' + + '\n' + + '

Navegar por cuadros de diálogo sin pestañas

\n' + + '\n' + + '

En los cuadros de diálogo sin pestañas, el primer componente interactivo se enfoca al abrirse el cuadro de diálogo.

\n' + + '\n' + + '

Para navegar entre los componentes interactivos del cuadro de diálogo, pulse las teclas Tab o Mayús+Tab.

\n' + + '\n' + + '

Navegar por cuadros de diálogo con pestañas

\n' + + '\n' + + '

En los cuadros de diálogo con pestañas, el primer botón del menú de pestaña se enfoca al abrirse el cuadro de diálogo.

\n' + + '\n' + + '

Para navegar entre componentes interactivos de esta pestaña del cuadro de diálogo, pulse las teclas Tab o\n' + + ' Mayús+Tab.

\n' + + '\n' + + '

Si desea cambiar a otra pestaña del cuadro de diálogo, enfoque el menú de pestañas y, a continuación, pulse la tecla de flecha\n' + + ' correspondiente para moverse por las pestañas disponibles.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/eu.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/eu.js new file mode 100644 index 0000000..c18b940 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/eu.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.eu', +'

Hasi teklatuaren nabigazioa

\n' + + '\n' + + '
\n' + + '
Fokuratu menu-barra
\n' + + '
Windows edo Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Fokuratu tresna-barra
\n' + + '
Windows edo Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Fokuratu orri-oina
\n' + + '
Windows edo Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Fokuratu jakinarazpena
\n' + + '
Windows edo Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Fokuratu testuinguruaren tresna-barra
\n' + + '
Windows, Linux edo macOS: Ktrl+F9
\n' + + '
\n' + + '\n' + + '

Nabigazioa EIko lehen elementuan hasiko da: elementu hori nabarmendu egingo da, edo azpimarratu lehen elementua bada\n' + + ' orri-oineko elementuaren bidea.

\n' + + '\n' + + '

Nabigatu EIko atalen artean

\n' + + '\n' + + '

EIko atal batetik hurrengora mugitzeko, sakatu Tabuladorea.

\n' + + '\n' + + '

EIko atal batetik aurrekora mugitzeko, sakatu Maius+Tabuladorea.

\n' + + '\n' + + '

EIko atal hauen Tabuladorea da:

\n' + + '\n' + + '
    \n' + + '
  1. Menu-barra
  2. \n' + + '
  3. Tresna-barraren talde bakoitza
  4. \n' + + '
  5. Alboko barra
  6. \n' + + '
  7. Orri-oineko elementuaren bidea
  8. \n' + + '
  9. Orri-oneko urrats-kontaketa txandakatzeko botoia
  10. \n' + + '
  11. Orri-oineko marken esteka
  12. \n' + + '
  13. Orri-oineko editorearen tamaina aldatzeko heldulekua
  14. \n' + + '
\n' + + '\n' + + '

EIko atal bat ez badago, saltatu egin da.

\n' + + '\n' + + '

Orri-oinak teklatuaren nabigazioa fokuratuta badago, eta alboko barra ikusgai ez badago, Maius+Tabuladorea sakatuz gero,\n' + + ' fokua tresna-barrako lehen taldera eramaten da, ez azkenera.

\n' + + '\n' + + '

Nabigatu EIko atalen barruan

\n' + + '\n' + + '

EIko elementu batetik hurrengora mugitzeko, sakatu dagokion Gezia tekla.

\n' + + '\n' + + '

Ezkerrera eta Eskuinera gezi-teklak

\n' + + '\n' + + '
    \n' + + '
  • menu-barrako menuen artean mugitzen da.
  • \n' + + '
  • ireki azpimenu bat menuan.
  • \n' + + '
  • mugitu botoi batetik bestera tresna-barren talde batean.
  • \n' + + '
  • mugitu orri-oineko elementuaren bideko elementu batetik bestera.
  • \n' + + '
\n' + + '\n' + + '

Gora eta Behera gezi-teklak

\n' + + '\n' + + '
    \n' + + '
  • mugitu menu bateko menu-elementuen artean.
  • \n' + + '
  • mugitu tresna-barrako menu gainerakor bateko menu-elementuen artean.
  • \n' + + '
\n' + + '\n' + + '

Gezia teklen zikloa nabarmendutako EI atalen barruan.

\n' + + '\n' + + '

Irekitako menu bat ixteko, ireki azpimenua, edo ireki menu gainerakorra, sakatu Ihes tekla.

\n' + + '\n' + + '

Une horretan fokuratzea EIko atal jakin baten "goialdean" badago, Ihes tekla sakatuz gero\n' + + ' teklatuaren nabigaziotik irtengo zara.

\n' + + '\n' + + '

Exekutatu menuko elementu bat edo tresna-barrako botoi bat

\n' + + '\n' + + '

Nahi den menuaren elementua edo tresna-barraren botoia nabarmenduta dagoenean, sakatu Itzuli, Sartu\n' + + ' edo Zuriune-barra elementua exekutatzeko.

\n' + + '\n' + + '

Nabigatu fitxarik gabeko elkarrizketak

\n' + + '\n' + + '

Fitxarik gabeko elkarrizketetan, lehen osagai interaktiboa fokuratzen da elkarrizketa irekitzen denean.

\n' + + '\n' + + '

Nabigatu elkarrizketa interaktiboko osagai batetik bestera Tabuladorea edo Maius+Tabuladorea sakatuta.

\n' + + '\n' + + '

Nabigatu fitxadun elkarrizketak

\n' + + '\n' + + '

Fitxadun elkarrizketetan, fitxa-menuko lehen botoia fokuratzen da elkarrizketa irekitzen denean.

\n' + + '\n' + + '

Nabigatu elkarrizketa-fitxa honen interaktiboko osagai batetik bestera Tabuladorea edo\n' + + ' Maius+Tabuladorea sakatuta.

\n' + + '\n' + + '

Aldatu beste elkarrizketa-fitxa batera fitxa-menua fokuratu eta dagokion Gezia\n' + + ' tekla sakatzeko, erabilgarri dauden fitxa batetik bestera txandakatzeko.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/fa.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/fa.js new file mode 100644 index 0000000..2a55012 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/fa.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.fa', +'

شروع پیمایش صفحه‌کلید

\n' + + '\n' + + '
\n' + + '
تمرکز بر نوار منو
\n' + + '
Windows یا Linux:‎‏: Alt+F9
\n' + + '
‎‏macOS: ⌥F9‎‏
\n' + + '
تمرکز بر نوار ابزار
\n' + + '
Windows یا Linux‎‏: Alt+F10
\n' + + '
‎‏macOS: ⌥F10‎‏
\n' + + '
تمرکز بر پانویس
\n' + + '
Windows یا Linux‎‏: Alt+F11
\n' + + '
‎‏macOS: ⌥F11‎‏
\n' + + '
تمرکز اعلان
\n' + + '
ویندوز یا لینوکس: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
تمرکز بر نوار ابزار بافتاری
\n' + + '
Windows ،Linux یا macOS:‏ Ctrl+F9
\n' + + '
\n' + + '\n' + + '

پیمایش در اولین مورد رابط کاربری شروع می‌شود و درخصوص اولین مورد در\n' + + ' مسیر عنصر پانویس، برجسته یا زیرخط‌دار می‌شود.

\n' + + '\n' + + '

پیمایش بین بخش‌های رابط کاربری

\n' + + '\n' + + '

برای جابجایی از یک بخش رابط کاربری به بخش بعدی، Tab را فشار دهید.

\n' + + '\n' + + '

برای جابجایی از یک بخش رابط کاربری به بخش قبلی، Shift+Tab را فشار دهید.

\n' + + '\n' + + '

ترتیب Tab این بخش‌های رابط کاربری عبارتند از:

\n' + + '\n' + + '
    \n' + + '
  1. نوار منو
  2. \n' + + '
  3. هر گروه نوار ابزار
  4. \n' + + '
  5. نوار کناری
  6. \n' + + '
  7. مسیر عنصر در پانویس
  8. \n' + + '
  9. دکمه تغییر وضعیت تعداد کلمات در پانویس
  10. \n' + + '
  11. پیوند نمانام‌سازی در پانویس
  12. \n' + + '
  13. دسته تغییر اندازه ویرایشگر در پانویس
  14. \n' + + '
\n' + + '\n' + + '

اگر بخشی از رابط کاربری موجود نباشد، رد می‌شود.

\n' + + '\n' + + '

اگر پانویس دارای تمرکز بر پیمایش صفحه‌کلید باشد،‌ و نوار کناری قابل‌مشاهده وجود ندارد، فشردن Shift+Tab\n' + + ' تمرکز را به گروه نوار ابزار اول می‌برد، نه آخر.

\n' + + '\n' + + '

پیمایش در بخش‌های رابط کاربری

\n' + + '\n' + + '

برای جابجایی از یک عنصر رابط کاربری به بعدی، کلید جهت‌نمای مناسب را فشار دهید.

\n' + + '\n' + + '

کلیدهای جهت‌نمای چپ و راست

\n' + + '\n' + + '
    \n' + + '
  • جابجایی بین منوها در نوار منو.
  • \n' + + '
  • باز کردن منوی فرعی در یک منو.
  • \n' + + '
  • جابجایی بین دکمه‌ها در یک گروه نوار ابزار.
  • \n' + + '
  • جابجایی بین موارد در مسیر عنصر پانویس.
  • \n' + + '
\n' + + '\n' + + '

کلیدهای جهت‌نمای پایین و بالا

\n' + + '\n' + + '
    \n' + + '
  • جابجایی بین موارد منو در یک منو.
  • \n' + + '
  • جابجایی بین موارد در یک منوی بازشوی نوار ابزار.
  • \n' + + '
\n' + + '\n' + + '

کلیدهایجهت‌نما در بخش رابط کاربری متمرکز می‌چرخند.

\n' + + '\n' + + '

برای بستن یک منوی باز، یک منوی فرعی باز، یا یک منوی بازشوی باز، کلید Esc را فشار دهید.

\n' + + '\n' + + '

اگر تمرکز فعلی در «بالای» یک بخش رابط کاربری خاص است، فشردن کلید Esc نیز موجب\n' + + ' خروج کامل از پیمایش صفحه‌کلید می‌شود.

\n' + + '\n' + + '

اجرای یک مورد منو یا دکمه نوار ابزار

\n' + + '\n' + + '

وقتی مورد منو یا دکمه نوار ابزار مورد نظر هایلایت شد، دکمه بازگشت، Enter،\n' + + ' یا نوار Space را فشار دهید تا مورد را اجرا کنید.

\n' + + '\n' + + '

پیمایش در کادرهای گفتگوی بدون زبانه

\n' + + '\n' + + '

در کادرهای گفتگوی بدون زبانه، وقتی کادر گفتگو باز می‌شود، اولین جزء تعاملی متمرکز می‌شود.

\n' + + '\n' + + '

با فشردن Tab یا Shift+Tab، بین اجزای کادر گفتگوی تعاملی پیمایش کنید.

\n' + + '\n' + + '

پیمایش کادرهای گفتگوی زبانه‌دار

\n' + + '\n' + + '

در کادرهای گفتگوی زبانه‌دار، وقتی کادر گفتگو باز می‌شود، اولین دکمه در منوی زبانه متمرکز می‌شود.

\n' + + '\n' + + '

با فشردن Tab یا\n' + + ' Shift+Tab، بین اجزای تعاملی این زبانه کادر گفتگو پیمایش کنید.

\n' + + '\n' + + '

با دادن تمرکز به منوی زبانه و سپس فشار دادن کلید جهت‌نمای\n' + + ' مناسب برای چرخش میان زبانه‌های موجود، به زبانه کادر گفتگوی دیگری بروید.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/fi.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/fi.js new file mode 100644 index 0000000..f01dc91 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/fi.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.fi', +'

Näppäimistönavigoinnin aloittaminen

\n' + + '\n' + + '
\n' + + '
Siirrä kohdistus valikkopalkkiin
\n' + + '
Windows tai Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Siirrä kohdistus työkalupalkkiin
\n' + + '
Windows tai Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Siirrä kohdistus alatunnisteeseen
\n' + + '
Windows tai Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Keskitä ilmoitukseen
\n' + + '
Windows ja Linux: Alt + F12
\n' + + '
macOS: ⌥F12
\n' + + '
Siirrä kohdistus kontekstuaaliseen työkalupalkkiin
\n' + + '
Windows, Linux tai macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

Navigointi aloitetaan ensimmäisestä käyttöliittymän kohteesta, joka joko korostetaan tai alleviivataan, jos\n' + + ' kyseessä on Alatunniste-elementin polun ensimmäinen kohde.

\n' + + '\n' + + '

Käyttöliittymän eri osien välillä navigointi

\n' + + '\n' + + '

Paina sarkainnäppäintä siirtyäksesi käyttöliittymän osasta seuraavaan.

\n' + + '\n' + + '

Jos haluat siirtyä edelliseen käyttöliittymän osaan, paina Shift+sarkainnäppäin.

\n' + + '\n' + + '

Sarkainnäppäin siirtää sinua näissä käyttöliittymän osissa tässä järjestyksessä:

\n' + + '\n' + + '
    \n' + + '
  1. Valikkopalkki
  2. \n' + + '
  3. Työkalupalkin ryhmät
  4. \n' + + '
  5. Sivupalkki
  6. \n' + + '
  7. Elementin polku alatunnisteessa
  8. \n' + + '
  9. Sanalaskurin vaihtopainike alatunnisteessa
  10. \n' + + '
  11. Brändäyslinkki alatunnisteessa
  12. \n' + + '
  13. Editorin koon muuttamisen kahva alatunnisteessa
  14. \n' + + '
\n' + + '\n' + + '

Jos jotakin käyttöliittymän osaa ei ole, se ohitetaan.

\n' + + '\n' + + '

Jos kohdistus on siirretty alatunnisteeseen näppäimistönavigoinnilla eikä sivupalkkia ole näkyvissä, Shift+sarkainnäppäin\n' + + ' siirtää kohdistuksen työkalupalkin ensimmäiseen ryhmään, eikä viimeiseen.

\n' + + '\n' + + '

Käyttöliittymän eri osien sisällä navigointi

\n' + + '\n' + + '

Paina nuolinäppäimiä siirtyäksesi käyttöliittymäelementistä seuraavaan.

\n' + + '\n' + + '

Vasen- ja Oikea-nuolinäppäimet

\n' + + '\n' + + '
    \n' + + '
  • siirtävät sinua valikkopalkin valikoiden välillä.
  • \n' + + '
  • avaavat valikon alavalikon.
  • \n' + + '
  • siirtävät sinua työkalupalkin ryhmän painikkeiden välillä.
  • \n' + + '
  • siirtävät sinua kohteiden välillä alatunnisteen elementin polussa.
  • \n' + + '
\n' + + '\n' + + '

Alas- ja Ylös-nuolinäppäimet

\n' + + '\n' + + '
    \n' + + '
  • siirtävät sinua valikon valikkokohteiden välillä.
  • \n' + + '
  • siirtävät sinua työkalupalkin ponnahdusvalikon kohteiden välillä.
  • \n' + + '
\n' + + '\n' + + '

Nuolinäppäimet siirtävät sinua käyttöliittymän korostetun osan sisällä syklissä.

\n' + + '\n' + + '

Paina Esc-näppäintä sulkeaksesi avoimen valikon, avataksesi alavalikon tai avataksesi ponnahdusvalikon.

\n' + + '\n' + + '

Jos kohdistus on käyttöliittymän tietyn osion ylälaidassa, Esc-näppäimen painaminen\n' + + ' poistuu myös näppäimistönavigoinnista kokonaan.

\n' + + '\n' + + '

Suorita valikkokohde tai työkalupalkin painike

\n' + + '\n' + + '

Kun haluamasi valikkokohde tai työkalupalkin painike on korostettuna, paina Return-, Enter-\n' + + ' tai välilyöntinäppäintä suorittaaksesi kohteen.

\n' + + '\n' + + '

Välilehdittömissä valintaikkunoissa navigointi

\n' + + '\n' + + '

Kun välilehdetön valintaikkuna avautuu, kohdistus siirtyy sen ensimmäiseen interaktiiviseen komponenttiin.

\n' + + '\n' + + '

Voit siirtyä valintaikkunan interaktiivisten komponenttien välillä painamalla sarkainnäppäintä tai Shift+sarkainnäppäin.

\n' + + '\n' + + '

Välilehdellisissä valintaikkunoissa navigointi

\n' + + '\n' + + '

Kun välilehdellinen valintaikkuna avautuu, kohdistus siirtyy välilehtivalikon ensimmäiseen painikkeeseen.

\n' + + '\n' + + '

Voit siirtyä valintaikkunan välilehden interaktiivisen komponenttien välillä painamalla sarkainnäppäintä tai\n' + + ' Shift+sarkainnäppäin.

\n' + + '\n' + + '

Voit siirtyä valintaikkunan toiseen välilehteen siirtämällä kohdistuksen välilehtivalikkoon ja painamalla sopivaa nuolinäppäintä\n' + + ' siirtyäksesi käytettävissä olevien välilehtien välillä syklissä.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/fr_FR.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/fr_FR.js new file mode 100644 index 0000000..3f611e8 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/fr_FR.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.fr_FR', +'

Débuter la navigation au clavier

\n' + + '\n' + + '
\n' + + '
Cibler la barre du menu
\n' + + '
Windows ou Linux : Alt+F9
\n' + + '
macOS : ⌥F9
\n' + + "
Cibler la barre d'outils
\n" + + '
Windows ou Linux : Alt+F10
\n' + + '
macOS : ⌥F10
\n' + + '
Cibler le pied de page
\n' + + '
Windows ou Linux : Alt+F11
\n' + + '
macOS : ⌥F11
\n' + + '
Cibler la notification
\n' + + '
Windows ou Linux : Alt+F12
\n' + + '
macOS : ⌥F12
\n' + + "
Cibler une barre d'outils contextuelle
\n" + + '
Windows, Linux ou macOS : Ctrl+F9
\n' + + '
\n' + + '\n' + + "

La navigation débutera sur le premier élément de l'interface utilisateur, qui sera mis en surbrillance ou bien souligné dans le cas du premier élément du\n" + + " chemin d'éléments du pied de page.

\n" + + '\n' + + "

Naviguer entre les sections de l'interface utilisateur

\n" + + '\n' + + "

Pour passer d'une section de l'interface utilisateur à la suivante, appuyez sur Tabulation.

\n" + + '\n' + + "

Pour passer d'une section de l'interface utilisateur à la précédente, appuyez sur Maj+Tabulation.

\n" + + '\n' + + "

L'ordre de Tabulation de ces sections de l'interface utilisateur est le suivant :

\n" + + '\n' + + '
    \n' + + '
  1. Barre du menu
  2. \n' + + "
  3. Chaque groupe de barres d'outils
  4. \n" + + '
  5. Barre latérale
  6. \n' + + "
  7. Chemin d'éléments du pied de page
  8. \n" + + "
  9. Bouton d'activation du compteur de mots dans le pied de page
  10. \n" + + '
  11. Lien de marque dans le pied de page
  12. \n' + + "
  13. Poignée de redimensionnement de l'éditeur dans le pied de page
  14. \n" + + '
\n' + + '\n' + + "

Si une section de l'interface utilisateur n'est pas présente, elle sera ignorée.

\n" + + '\n' + + "

Si le pied de page comporte un ciblage par navigation au clavier et qu'il n'y a aucune barre latérale visible, appuyer sur Maj+Tabulation\n" + + " déplace le ciblage vers le premier groupe de barres d'outils et non le dernier.

\n" + + '\n' + + "

Naviguer au sein des sections de l'interface utilisateur

\n" + + '\n' + + "

Pour passer d'un élément de l'interface utilisateur au suivant, appuyez sur la Flèche appropriée.

\n" + + '\n' + + '

Les touches fléchées Gauche et Droite

\n' + + '\n' + + '
    \n' + + '
  • se déplacent entre les menus de la barre des menus.
  • \n' + + "
  • ouvrent un sous-menu au sein d'un menu.
  • \n" + + "
  • se déplacent entre les boutons d'un groupe de barres d'outils.
  • \n" + + "
  • se déplacent entre les éléments du chemin d'éléments du pied de page.
  • \n" + + '
\n' + + '\n' + + '

Les touches fléchées Bas et Haut

\n' + + '\n' + + '
    \n' + + "
  • se déplacent entre les éléments de menu au sein d'un menu.
  • \n" + + "
  • se déplacent entre les éléments au sein d'un menu contextuel de barre d'outils.
  • \n" + + '
\n' + + '\n' + + "

Les Flèches parcourent la section de l'interface utilisateur ciblée.

\n" + + '\n' + + '

Pour fermer un menu ouvert, un sous-menu ouvert ou un menu contextuel ouvert, appuyez sur Echap.

\n' + + '\n' + + "

Si l'actuel ciblage se trouve en « haut » d'une section spécifique de l'interface utilisateur, appuyer sur Echap permet également de quitter\n" + + ' entièrement la navigation au clavier.

\n' + + '\n' + + "

Exécuter un élément de menu ou un bouton de barre d'outils

\n" + + '\n' + + "

Lorsque l'élément de menu ou le bouton de barre d'outils désiré est mis en surbrillance, appuyez sur la touche Retour arrière, Entrée\n" + + " ou la Barre d'espace pour exécuter l'élément.

\n" + + '\n' + + '

Naviguer au sein de dialogues sans onglets

\n' + + '\n' + + "

Dans les dialogues sans onglets, le premier composant interactif est ciblé lorsque le dialogue s'ouvre.

\n" + + '\n' + + '

Naviguez entre les composants du dialogue interactif en appuyant sur Tabulation ou Maj+Tabulation.

\n' + + '\n' + + '

Naviguer au sein de dialogues avec onglets

\n' + + '\n' + + "

Dans les dialogues avec onglets, le premier bouton du menu de l'onglet est ciblé lorsque le dialogue s'ouvre.

\n" + + '\n' + + '

Naviguez entre les composants interactifs de cet onglet de dialogue en appuyant sur Tabulation ou\n' + + ' Maj+Tabulation.

\n' + + '\n' + + "

Passez à un autre onglet de dialogue en ciblant le menu de l'onglet et en appuyant sur la Flèche\n" + + ' appropriée pour parcourir les onglets disponibles.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/he_IL.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/he_IL.js new file mode 100644 index 0000000..7d6513a --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/he_IL.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.he_IL', +'

התחל ניווט במקלדת

\n' + + '\n' + + '
\n' + + '
התמקד בשורת התפריטים
\n' + + '
Windows או Linux:‏ Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
העבר מיקוד לסרגל הכלים
\n' + + '
Windows או Linux:‏ Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
העבר מיקוד לכותרת התחתונה
\n' + + '
Windows או Linux:‏ Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
העבר מיקוד להודעה
\n' + + '
Windows או Linux:‏ Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
העבר מיקוד לסרגל כלים הקשרי
\n' + + '
Windows‏, Linux או macOS:‏ Ctrl+F9
\n' + + '
\n' + + '\n' + + '

הניווט יתחיל ברכיב הראשון במשך, שיודגש או שיהיה מתחתיו קו תחתון במקרה של הפריט הראשון\n' + + ' הנתיב של רכיב הכותרת התחתונה.

\n' + + '\n' + + '

עבור בין מקטעים במסך

\n' + + '\n' + + '

כדי לעבור בין המקטעים במסך, הקש Tab.

\n' + + '\n' + + '

כדי לעבור למקטע הקודם במסך, הקש Shift+Tab.

\n' + + '\n' + + '

הסדר מבחינת מקש Tab של הרכיבים במסך:

\n' + + '\n' + + '
    \n' + + '
  1. שורת התפריטים
  2. \n' + + '
  3. כל קבוצה בסרגל הכלים
  4. \n' + + '
  5. הסרגל הצידי
  6. \n' + + '
  7. נתיב של רכיב בכותרת התחתונה
  8. \n' + + '
  9. לחצן לספירת מילים בכותרת התחתונה
  10. \n' + + '
  11. קישור של המותג בכותרת התחתונה
  12. \n' + + '
  13. ידית לשינוי גודל עבור העורך בכותרת התחתונה
  14. \n' + + '
\n' + + '\n' + + '

אם רכיב כלשהו במסך לא מופיע, המערכת תדלג עליו.

\n' + + '\n' + + '

אם בכותרת התחתונה יש מיקוד של ניווט במקלדת, ולא מופיע סרגל בצד, יש להקיש Shift+Tab\n' + + ' מעביר את המיקוד לקבוצה הראשונה בסרגל הכלים, לא האחרונה.

\n' + + '\n' + + '

עבור בתוך מקטעים במסך

\n' + + '\n' + + '

כדי לעבור מרכיב אחד לרכיב אחר במסך, הקש על מקש החץ המתאים.

\n' + + '\n' + + '

מקשי החיצים שמאלה וימינה

\n' + + '\n' + + '
    \n' + + '
  • עבור בין תפריטים בשורת התפריטים.
  • \n' + + '
  • פתח תפריט משני בתפריט.
  • \n' + + '
  • עבור בין לחצנים בקבוצה בסרגל הכלים.
  • \n' + + '
  • עבור בין פריטים ברכיב בכותרת התחתונה.
  • \n' + + '
\n' + + '\n' + + '

מקשי החיצים למטה ולמעלה

\n' + + '\n' + + '
    \n' + + '
  • עבור בין פריטים בתפריט.
  • \n' + + '
  • עבור בין פריטים בחלון הקובץ של סרגל הכלים.
  • \n' + + '
\n' + + '\n' + + '

מקשי החצים משתנים בתוך המקטע במסך שעליו נמצא המיקוד.

\n' + + '\n' + + '

כדי לסגור תפריט פתוח, תפריט משני פתוח או חלון קופץ, הקש על Esc.

\n' + + '\n' + + "

אם המיקוד הוא על החלק 'העליון' של מקטע מסוים במסך, הקשה על Esc מביאה גם ליציאה\n" + + ' מהניווט במקלדת לחלוטין.

\n' + + '\n' + + '

הפעל פריט בתפריט או לחצן בסרגל הכלים

\n' + + '\n' + + '

כאשר הפריט הרצוי בתפריט או הלחצן בסרגל הכלים מודגשים, הקש על Return, Enter,\n' + + ' או על מקש הרווח כדי להפעיל את הפריט.

\n' + + '\n' + + '

ניווט בחלונות דו-שיח בלי כרטיסיות

\n' + + '\n' + + '

בחלונות דו-שיח בלי כרטיסיות, הרכיב האינטראקטיבי הראשון מקבל את המיקוד כאשר החלון נפתח.

\n' + + '\n' + + '

עבור בין רכיבים אינטראקטיביים בחלון על ידי הקשה על Tab או Shift+Tab.

\n' + + '\n' + + '

ניווט בחלונות דו-שיח עם כרטיסיות

\n' + + '\n' + + '

בחלונות דו-שיח עם כרטיסיות, הלחצן הראשון בתפריט מקבל את המיקוד כאשר החלון נפתח.

\n' + + '\n' + + '

עבור בין רכיבים אינטראקטיביים בחלון על ידי הקשה על Tab או\n' + + ' Shift+Tab.

\n' + + '\n' + + '

עבור לכרטיסיה אחרת בחלון על ידי העברת המיקוד לתפריט הכרטיסיות והקשה על החץהמתאים\n' + + ' כדי לעבור בין הכרטיסיות הזמינות.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/hi.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/hi.js new file mode 100644 index 0000000..ef59a5c --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/hi.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.hi', +'

कीबोर्ड नेविगेशन शुरू करें

\n' + + '\n' + + '
\n' + + '
मेन्यू बार पर फ़ोकस करें
\n' + + '
Windows या Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
टूलबार पर फ़ोकस करें
\n' + + '
Windows या Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
फ़ुटर पर फ़ोकस करें
\n' + + '
Windows या Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
नोटिफ़िकेशन फ़ोकस
\n' + + '
Windows या Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
प्रासंगिक टूलबार पर फ़ोकस करें
\n' + + '
Windows, Linux या macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

नेविगेशन पहले UI आइटम पर शुरू होगा, जिसे हाइलाइट किया जाएगा या पहले आइटम के मामले में फ़ुटर तत्व पथ में\n' + + ' रेखांकित किया जाएगा।

\n' + + '\n' + + '

UI सेक्शन के बीच नेविगेट करें

\n' + + '\n' + + '

एक UI सेक्शन से दूसरे सेक्शन में जाने के लिए, Tab दबाएं।

\n' + + '\n' + + '

एक UI सेक्शन से पिछले सेक्शन में जाने के लिए, Shift+Tab दबाएं।

\n' + + '\n' + + '

इन UI सेक्शन का Tab क्रम नीचे दिया गया है:

\n' + + '\n' + + '
    \n' + + '
  1. मेन्यू बार
  2. \n' + + '
  3. प्रत्येक टूलबार समूह
  4. \n' + + '
  5. साइडबार
  6. \n' + + '
  7. फ़ुटर में तत्व पथ
  8. \n' + + '
  9. फ़ुटर में शब्द गणना टॉगल बटन
  10. \n' + + '
  11. फ़ुटर में ब्रांडिंग लिंक
  12. \n' + + '
  13. फ़ुटर में संपादक का आकार बदलने का हैंडल
  14. \n' + + '
\n' + + '\n' + + '

अगर कोई UI सेक्शन मौजूद नहीं है, तो उसे छोड़ दिया जाता है।

\n' + + '\n' + + '

अगर फ़ुटर में कीबोर्ड नेविगेशन फ़ोकस है, और कोई दिखा देने वाला साइडबार नहीं है, तो Shift+Tab दबाने से\n' + + ' फ़ोकस पहले टूलबार समूह पर चला जाता है, पिछले पर नहीं।

\n' + + '\n' + + '

UI सेक्शन के भीतर नेविगेट करें

\n' + + '\n' + + '

एक UI तत्व से दूसरे में जाने के लिए उपयुक्त ऐरो कुंजी दबाएं।

\n' + + '\n' + + '

बाएं और दाएं ऐरो कुंजियां

\n' + + '\n' + + '
    \n' + + '
  • मेन्यू बार में मेन्यू के बीच ले जाती हैं।
  • \n' + + '
  • मेन्यू में एक सब-मेन्यू खोलें।
  • \n' + + '
  • टूलबार समूह में बटनों के बीच ले जाएं।
  • \n' + + '
  • फ़ुटर के तत्व पथ में आइटम के बीच ले जाएं।
  • \n' + + '
\n' + + '\n' + + '

नीचे और ऊपर ऐरो कुंजियां

\n' + + '\n' + + '
    \n' + + '
  • मेन्यू में मेन्यू आइटम के बीच ले जाती हैं।
  • \n' + + '
  • टूलबार पॉप-अप मेन्यू में आइटम के बीच ले जाएं।
  • \n' + + '
\n' + + '\n' + + '

फ़ोकस वाले UI सेक्शन के भीतर ऐरो कुंजियां चलाती रहती हैं।

\n' + + '\n' + + '

कोई खुला मेन्यू, कोई खुला सब-मेन्यू या कोई खुला पॉप-अप मेन्यू बंद करने के लिए Esc कुंजी दबाएं।

\n' + + '\n' + + "

अगर मौजूदा फ़ोकस किसी विशेष UI सेक्शन के 'शीर्ष' पर है, तो Esc कुंजी दबाने से भी\n" + + ' कीबोर्ड नेविगेशन पूरी तरह से बाहर हो जाता है।

\n' + + '\n' + + '

मेन्यू आइटम या टूलबार बटन निष्पादित करें

\n' + + '\n' + + '

जब वांछित मेन्यू आइटम या टूलबार बटन हाइलाइट किया जाता है, तो आइटम को निष्पादित करने के लिए Return, Enter,\n' + + ' या Space bar दबाएं।

\n' + + '\n' + + '

गैर-टैब वाले डायलॉग पर नेविगेट करें

\n' + + '\n' + + '

गैर-टैब वाले डायलॉग में, डायलॉग खुलने पर पहला इंटरैक्टिव घटक फ़ोकस लेता है।

\n' + + '\n' + + '

Tab or Shift+Tab दबाकर इंटरैक्टिव डायलॉग घटकों के बीच नेविगेट करें।

\n' + + '\n' + + '

टैब किए गए डायलॉग पर नेविगेट करें

\n' + + '\n' + + '

टैब किए गए डायलॉग में, डायलॉग खुलने पर टैब मेन्यू में पहला बटन फ़ोकस लेता है।

\n' + + '\n' + + '

इस डायलॉग टैब के इंटरैक्टिव घटकों के बीच नेविगेट करने के लिए Tab या\n' + + ' Shift+Tab दबाएं।

\n' + + '\n' + + '

टैब मेन्यू को फ़ोकस देकर और फिर उपलब्ध टैब में के बीच जाने के लिए उपयुक्त ऐरो\n' + + ' कुंजी दबाकर दूसरे डायलॉग टैब पर स्विच करें।

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/hr.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/hr.js new file mode 100644 index 0000000..1bf35c5 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/hr.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.hr', +'

Početak navigacije na tipkovnici

\n' + + '\n' + + '
\n' + + '
Fokusiranje trake izbornika
\n' + + '
Windows ili Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Fokusiranje alatne trake
\n' + + '
Windows ili Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Fokusiranje podnožja
\n' + + '
Windows ili Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Fokusiranje obavijesti
\n' + + '
Windows ili Linux: Alt + F12
\n' + + '
macOS: ⌥F12
\n' + + '
Fokusiranje kontekstne alatne trake
\n' + + '
Windows, Linux ili macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

Navigacija će započeti kod prve stavke na korisničkom sučelju, koja će biti istaknuta ili podcrtana ako se radi o prvoj stavci u\n' + + ' putu elementa u podnožju.

\n' + + '\n' + + '

Navigacija između dijelova korisničkog sučelja

\n' + + '\n' + + '

Za pomicanje s jednog dijela korisničkog sučelja na drugi pritisnite tabulator.

\n' + + '\n' + + '

Za pomicanje s jednog dijela korisničkog sučelja na prethodni pritisnite Shift + tabulator.

\n' + + '\n' + + '

Ovo je redoslijed pomicanja tabulatora po dijelovima korisničkog sučelja:

\n' + + '\n' + + '
    \n' + + '
  1. Traka izbornika
  2. \n' + + '
  3. Pojedinačne grupe na alatnoj traci
  4. \n' + + '
  5. Bočna traka
  6. \n' + + '
  7. Put elemenata u podnožju
  8. \n' + + '
  9. Gumb za pomicanje po broju riječi u podnožju
  10. \n' + + '
  11. Veza na brand u podnožju
  12. \n' + + '
  13. Značajka za promjenu veličine alata za uređivanje u podnožju
  14. \n' + + '
\n' + + '\n' + + '

Ako neki dio korisničkog sučelja nije naveden, on se preskače.

\n' + + '\n' + + '

Ako u podnožju postoji fokus za navigaciju na tipkovnici, a nema vidljive bočne trake, pritiskom na Shift + tabulator\n' + + ' fokus se prebacuje na prvu skupinu na alatnoj traci, ne na zadnju.

\n' + + '\n' + + '

Navigacija unutar dijelova korisničkog sučelja

\n' + + '\n' + + '

Za pomicanje s jednog elementa korisničkog sučelja na drugi pritisnite tipku s odgovarajućom strelicom.

\n' + + '\n' + + '

Tipke s lijevom i desnom strelicom

\n' + + '\n' + + '
    \n' + + '
  • služe za pomicanje između izbornika na alatnoj traci.
  • \n' + + '
  • otvaraju podizbornik unutar izbornika.
  • \n' + + '
  • služe za pomicanje između gumba unutar skupina na alatnoj traci.
  • \n' + + '
  • služe za pomicanje između stavki na elementu puta u podnožju.
  • \n' + + '
\n' + + '\n' + + '

Tipke s donjom i gornjom strelicom

\n' + + '\n' + + '
    \n' + + '
  • služe za pomicanje između stavki unutar izbornika.
  • \n' + + '
  • služe za pomicanje između stavki na alatnoj traci skočnog izbornika.
  • \n' + + '
\n' + + '\n' + + '

Tipkama strelica kružno se pomičete unutar dijela korisničkog sučelja koji je u fokusu.

\n' + + '\n' + + '

Za zatvaranje otvorenog izbornika, otvorenog podizbornika ili otvorenog skočnog izbornika pritisnite tipku Esc.

\n' + + '\n' + + '

Ako je fokus trenutačno postavljen na vrh pojedinačnog dijela korisničkog sučelja, pritiskom na tipku Esc također\n' + + ' u potpunosti zatvarate navigaciju na tipkovnici.

\n' + + '\n' + + '

Izvršavanje radnji putem stavki izbornika ili gumba na alatnoj traci

\n' + + '\n' + + '

Nakon što se istakne stavka izbornika ili gumb na alatnoj traci s radnjom koju želite izvršiti, pritisnite tipku Return, Enter\n' + + ' ili razmak da biste pokrenuli željenu radnju.

\n' + + '\n' + + '

Navigacija dijaloškim okvirima izvan kartica

\n' + + '\n' + + '

Prilikom otvaranja dijaloških okvira izvan kartica fokus se nalazi na prvoj interaktivnoj komponenti.

\n' + + '\n' + + '

Navigaciju između interaktivnih dijaloških komponenata vršite pritiskom na tabulator ili Shift + tabulator.

\n' + + '\n' + + '

Navigacija dijaloškim okvirima u karticama

\n' + + '\n' + + '

Prilikom otvaranja dijaloških okvira u karticama fokus se nalazi na prvom gumbu u izborniku unutar kartice.

\n' + + '\n' + + '

Navigaciju između interaktivnih komponenata dijaloškog okvira u kartici vršite pritiskom na tabulator ili\n' + + ' Shift + tabulator.

\n' + + '\n' + + '

Na karticu s drugim dijaloškim okvirom možete se prebaciti tako da stavite fokus na izbornik kartice pa pritisnete tipku s odgovarajućom strelicom\n' + + ' za kružno pomicanje između dostupnih kartica.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/hu_HU.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/hu_HU.js new file mode 100644 index 0000000..5c984bb --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/hu_HU.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.hu_HU', +'

Billentyűzetes navigáció indítása

\n' + + '\n' + + '
\n' + + '
Fókusz a menüsávra
\n' + + '
Windows és Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Fókusz az eszköztárra
\n' + + '
Windows és Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Fókusz a láblécre
\n' + + '
Windows és Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Ráközelítés az értesítésre
\n' + + '
Windows vagy Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Fókusz egy környezetfüggő eszköztárra
\n' + + '
Windows, Linux és macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

A navigáció az első felhasználói felületi elemnél kezdődik, amelyet a rendszer kiemel, illetve aláhúz, amennyiben az az első elem\n' + + ' a lábléc elemútvonalán.

\n' + + '\n' + + '

Navigálás a felhasználói felület szakaszai között

\n' + + '\n' + + '

A felhasználói felület következő szakaszára váltáshoz nyomja meg a Tab billentyűt.

\n' + + '\n' + + '

A felhasználói felület előző szakaszára váltáshoz nyomja meg a Shift+Tab billentyűt.

\n' + + '\n' + + '

A Tab billentyűvel a felhasználói felület szakaszai között a következő sorrendben vált:

\n' + + '\n' + + '
    \n' + + '
  1. Menüsáv
  2. \n' + + '
  3. Az egyes eszköztárcsoportok
  4. \n' + + '
  5. Oldalsáv
  6. \n' + + '
  7. Elemútvonal a láblécen
  8. \n' + + '
  9. Szószámátkapcsoló gomb a láblécen
  10. \n' + + '
  11. Márkalink a láblécen
  12. \n' + + '
  13. Szerkesztő átméretezési fogópontja a láblécen
  14. \n' + + '
\n' + + '\n' + + '

Ha a felhasználói felület valamelyik eleme nincs jelen, a rendszer kihagyja.

\n' + + '\n' + + '

Ha a billentyűzetes navigáció fókusza a láblécen van, és nincs látható oldalsáv, a Shift+Tab\n' + + ' billentyűkombináció lenyomásakor az első eszköztárcsoportra ugrik a fókusz, nem az utolsóra.

\n' + + '\n' + + '

Navigálás a felhasználói felület szakaszain belül

\n' + + '\n' + + '

A felhasználói felület következő elemére váltáshoz nyomja meg a megfelelő nyílbillentyűt.

\n' + + '\n' + + '

A bal és a jobb nyílgomb

\n' + + '\n' + + '
    \n' + + '
  • a menüsávban a menük között vált.
  • \n' + + '
  • a menükben megnyit egy almenüt.
  • \n' + + '
  • az eszköztárcsoportban a gombok között vált.
  • \n' + + '
  • a lábléc elemútvonalán az elemek között vált.
  • \n' + + '
\n' + + '\n' + + '

A le és a fel nyílgomb

\n' + + '\n' + + '
    \n' + + '
  • a menükben a menüpontok között vált.
  • \n' + + '
  • az eszköztár előugró menüjében az elemek között vált.
  • \n' + + '
\n' + + '\n' + + '

A nyílbillentyűk lenyomásával körkörösen lépkedhet a fókuszban lévő felhasználói felületi szakasz elemei között.

\n' + + '\n' + + '

A megnyitott menüket, almenüket és előugró menüket az Esc billentyűvel zárhatja be.

\n' + + '\n' + + '

Ha a fókusz az aktuális felületi elem „felső” részén van, az Esc billentyűvel az egész\n' + + ' billentyűzetes navigációból kilép.

\n' + + '\n' + + '

Menüpont vagy eszköztárgomb aktiválása

\n' + + '\n' + + '

Amikor a kívánt menüelem vagy eszköztárgomb van kijelölve, nyomja meg a Return, az Enter\n' + + ' vagy a Szóköz billentyűt az adott elem vagy gomb aktiválásához.

\n' + + '\n' + + '

Navigálás a lapokkal nem rendelkező párbeszédablakokban

\n' + + '\n' + + '

A lapokkal nem rendelkező párbeszédablakokban az első interaktív összetevő kapja a fókuszt, amikor a párbeszédpanel megnyílik.

\n' + + '\n' + + '

A párbeszédpanelek interaktív összetevői között a Tab vagy a Shift+Tab billentyűvel navigálhat.

\n' + + '\n' + + '

Navigálás a lapokkal rendelkező párbeszédablakokban

\n' + + '\n' + + '

A lapokkal rendelkező párbeszédablakokban a lapmenü első gombja kapja a fókuszt, amikor a párbeszédpanel megnyílik.

\n' + + '\n' + + '

A párbeszédpanel e lapjának interaktív összetevői között a Tab vagy\n' + + ' Shift+Tab billentyűvel navigálhat.

\n' + + '\n' + + '

A párbeszédablak másik lapjára úgy léphet, hogy a fókuszt a lapmenüre állítja, majd lenyomja a megfelelő nyílbillentyűt\n' + + ' a rendelkezésre álló lapok közötti lépkedéshez.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/id.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/id.js new file mode 100644 index 0000000..d607dd1 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/id.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.id', +'

Memulai navigasi keyboard

\n' + + '\n' + + '
\n' + + '
Fokus pada bilah Menu
\n' + + '
Windows atau Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Fokus pada Bilah Alat
\n' + + '
Windows atau Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Fokus pada footer
\n' + + '
Windows atau Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Fokuskan pemberitahuan
\n' + + '
Windows atau Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Fokus pada bilah alat kontekstual
\n' + + '
Windows, Linux, atau macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

Navigasi akan dimulai dari item pertama UI, yang akan disorot atau digarisbawahi di\n' + + ' alur elemen Footer.

\n' + + '\n' + + '

Berpindah antar-bagian UI

\n' + + '\n' + + '

Untuk berpindah dari satu bagian UI ke bagian berikutnya, tekan Tab.

\n' + + '\n' + + '

Untuk berpindah dari satu bagian UI ke bagian sebelumnya, tekan Shift+Tab.

\n' + + '\n' + + '

Urutan Tab bagian-bagian UI ini adalah:

\n' + + '\n' + + '
    \n' + + '
  1. Bilah menu
  2. \n' + + '
  3. Tiap grup bilah alat
  4. \n' + + '
  5. Bilah sisi
  6. \n' + + '
  7. Alur elemen di footer
  8. \n' + + '
  9. Tombol aktifkan/nonaktifkan jumlah kata di footer
  10. \n' + + '
  11. Tautan merek di footer
  12. \n' + + '
  13. Pengatur pengubahan ukuran editor di footer
  14. \n' + + '
\n' + + '\n' + + '

Jika suatu bagian UI tidak ada, bagian tersebut dilewati.

\n' + + '\n' + + '

Jika fokus navigasi keyboard ada pada footer, tetapi tidak ada bilah sisi yang terlihat, menekan Shift+Tab\n' + + ' akan memindahkan fokus ke grup bilah alat pertama, bukan yang terakhir.

\n' + + '\n' + + '

Berpindah di dalam bagian-bagian UI

\n' + + '\n' + + '

Untuk berpindah dari satu elemen UI ke elemen berikutnya, tekan tombol Panah yang sesuai.

\n' + + '\n' + + '

Tombol panah Kiri dan Kanan untuk

\n' + + '\n' + + '
    \n' + + '
  • berpindah-pindah antar-menu di dalam bilah menu.
  • \n' + + '
  • membuka sub-menu di dalam menu.
  • \n' + + '
  • berpindah-pindah antar-tombol di dalam grup bilah alat.
  • \n' + + '
  • berpindah-pindah antar-item di dalam alur elemen footer.
  • \n' + + '
\n' + + '\n' + + '

Tombol panah Bawah dan Atas untuk

\n' + + '\n' + + '
    \n' + + '
  • berpindah-pindah antar-item menu di dalam menu.
  • \n' + + '
  • berpindah-pindah antar-item di dalam menu pop-up bilah alat.
  • \n' + + '
\n' + + '\n' + + '

Tombol Panah hanya bergerak di dalam bagian UI yang difokuskan.

\n' + + '\n' + + '

Untuk menutup menu, sub-menu, atau menu pop-up yang terbuka, tekan tombol Esc.

\n' + + '\n' + + '

Jika fokus sedang berada di ‘atas’ bagian UI tertentu, menekan tombol Esc juga dapat mengeluarkan fokus\n' + + ' dari seluruh navigasi keyboard.

\n' + + '\n' + + '

Menjalankan item menu atau tombol bilah alat

\n' + + '\n' + + '

Jika item menu atau tombol bilah alat yang diinginkan tersorot, tekan Return, Enter,\n' + + ' atau Spasi untuk menjalankan item.

\n' + + '\n' + + '

Berpindah dalam dialog tanpa tab

\n' + + '\n' + + '

Dalam dialog tanpa tab, fokus diarahkan pada komponen interaktif pertama saat dialog terbuka.

\n' + + '\n' + + '

Berpindah di antara komponen dalam dialog interaktif dengan menekan Tab atau Shift+Tab.

\n' + + '\n' + + '

Berpindah dalam dialog dengan tab

\n' + + '\n' + + '

Dalam dialog yang memiliki tab, fokus diarahkan pada tombol pertama di dalam menu saat dialog terbuka.

\n' + + '\n' + + '

Berpindah di antara komponen-komponen interaktif pada tab dialog ini dengan menekan Tab atau\n' + + ' Shift+Tab.

\n' + + '\n' + + '

Beralih ke tab dialog lain dengan mengarahkan fokus pada menu tab lalu tekan tombol Panah\n' + + ' yang sesuai untuk berpindah ke berbagai tab yang tersedia.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/it.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/it.js new file mode 100644 index 0000000..3a791c9 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/it.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.it', +'

Iniziare la navigazione tramite tastiera

\n' + + '\n' + + '
\n' + + '
Impostare lo stato attivo per la barra dei menu
\n' + + '
Windows o Linux: ALT+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Impostare lo stato attivo per la barra degli strumenti
\n' + + '
Windows o Linux: ALT+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Impostare lo stato attivo per il piè di pagina
\n' + + '
Windows o Linux: ALT+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Metti a fuoco la notifica
\n' + + '
Windows o Linux: ALT+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Impostare lo stato attivo per la barra degli strumenti contestuale
\n' + + '
Windows, Linux o macOS: CTRL+F9
\n' + + '
\n' + + '\n' + + "

La navigazione inizierà dalla prima voce dell'interfaccia utente, che sarà evidenziata o sottolineata nel caso della prima voce\n" + + " nel percorso dell'elemento del piè di pagina.

\n" + + '\n' + + "

Navigare tra le sezioni dell'interfaccia utente

\n" + + '\n' + + "

Per passare da una sezione dell'interfaccia utente alla successiva, premere TAB.

\n" + + '\n' + + "

Per passare da una sezione dell'interfaccia utente alla precedente, premere MAIUSC+TAB.

\n" + + '\n' + + "

L'ordine di tabulazione di queste sezioni dell'interfaccia utente è:

\n" + + '\n' + + '
    \n' + + '
  1. Barra dei menu
  2. \n' + + '
  3. Ogni gruppo di barre degli strumenti
  4. \n' + + '
  5. Barra laterale
  6. \n' + + "
  7. Percorso dell'elemento nel piè di pagina
  8. \n" + + '
  9. Pulsante di attivazione/disattivazione del conteggio delle parole nel piè di pagina
  10. \n' + + '
  11. Collegamento al marchio nel piè di pagina
  12. \n' + + "
  13. Quadratino di ridimensionamento dell'editor nel piè di pagina
  14. \n" + + '
\n' + + '\n' + + "

Se una sezione dell'interfaccia utente non è presente, viene saltata.

\n" + + '\n' + + '

Se il piè di pagina ha lo stato attivo per la navigazione tramite tastiera e non è presente alcuna barra laterale visibile, premendo MAIUSC+TAB\n' + + " si sposta lo stato attivo sul primo gruppo di barre degli strumenti, non sull'ultimo.

\n" + + '\n' + + "

Navigare all'interno delle sezioni dell'interfaccia utente

\n" + + '\n' + + "

Per passare da un elemento dell'interfaccia utente al successivo, premere il tasto freccia appropriato.

\n" + + '\n' + + '

I tasti freccia Sinistra e Destra

\n' + + '\n' + + '
    \n' + + '
  • consentono di spostarsi tra i menu della barra dei menu.
  • \n' + + '
  • aprono un sottomenu in un menu.
  • \n' + + '
  • consentono di spostarsi tra i pulsanti di un gruppo di barre degli strumenti.
  • \n' + + "
  • consentono di spostarsi tra le voci nel percorso dell'elemento del piè di pagina.
  • \n" + + '
\n' + + '\n' + + '

I tasti freccia Giù e Su

\n' + + '\n' + + '
    \n' + + '
  • consentono di spostarsi tra le voci di un menu.
  • \n' + + '
  • consentono di spostarsi tra le voci di un menu a comparsa della barra degli strumenti.
  • \n' + + '
\n' + + '\n' + + "

I tasti freccia consentono di spostarsi all'interno della sezione dell'interfaccia utente con stato attivo.

\n" + + '\n' + + '

Per chiudere un menu aperto, un sottomenu aperto o un menu a comparsa aperto, premere il tasto ESC.

\n' + + '\n' + + "

Se lo stato attivo corrente si trova nella parte superiore di una particolare sezione dell'interfaccia utente, premendo il tasto ESC si esce\n" + + ' completamente dalla navigazione tramite tastiera.

\n' + + '\n' + + '

Eseguire una voce di menu o un pulsante della barra degli strumenti

\n' + + '\n' + + '

Quando la voce di menu o il pulsante della barra degli strumenti desiderati sono evidenziati, premere il tasto diritorno a capo, il tasto Invio\n' + + ' o la barra spaziatrice per eseguirli.

\n' + + '\n' + + '

Navigare nelle finestre di dialogo non a schede

\n' + + '\n' + + "

Nelle finestre di dialogo non a schede, all'apertura della finestra di dialogo diventa attivo il primo componente interattivo.

\n" + + '\n' + + '

Per spostarsi tra i componenti interattivi della finestra di dialogo, premere TAB o MAIUSC+TAB.

\n' + + '\n' + + '

Navigare nelle finestre di dialogo a schede

\n' + + '\n' + + "

Nelle finestre di dialogo a schede, all'apertura della finestra di dialogo diventa attivo il primo pulsante del menu della scheda.

\n" + + '\n' + + '

Per spostarsi tra i componenti interattivi di questa scheda della finestra di dialogo, premere TAB o\n' + + ' MAIUSC+TAB.

\n' + + '\n' + + "

Per passare a un'altra scheda della finestra di dialogo, attivare il menu della scheda e premere il tasto freccia\n" + + ' appropriato per scorrere le schede disponibili.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/ja.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/ja.js new file mode 100644 index 0000000..26872db --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/ja.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.ja', +'

キーボード ナビゲーションの開始

\n' + + '\n' + + '
\n' + + '
メニュー バーをフォーカス
\n' + + '
Windows または Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
ツール バーをフォーカス
\n' + + '
Windows または Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
フッターをフォーカス
\n' + + '
Windows または Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
通知にフォーカス
\n' + + '
Windows または Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
コンテキスト ツール バーをフォーカス
\n' + + '
Windows、Linux または macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

ナビゲーションは最初の UI 項目から開始され、強調表示されるか、フッターの要素パスにある最初の項目の場合は\n' + + ' 下線が引かれます。

\n' + + '\n' + + '

UI セクション間の移動

\n' + + '\n' + + '

次の UI セクションに移動するには、Tab を押します。

\n' + + '\n' + + '

前の UI セクションに移動するには、Shift+Tab を押します。

\n' + + '\n' + + '

これらの UI セクションの Tab の順序:

\n' + + '\n' + + '
    \n' + + '
  1. メニュー バー
  2. \n' + + '
  3. 各ツール バー グループ
  4. \n' + + '
  5. サイド バー
  6. \n' + + '
  7. フッターの要素パス
  8. \n' + + '
  9. フッターの単語数切り替えボタン
  10. \n' + + '
  11. フッターのブランド リンク
  12. \n' + + '
  13. フッターのエディター サイズ変更ハンドル
  14. \n' + + '
\n' + + '\n' + + '

UI セクションが存在しない場合は、スキップされます。

\n' + + '\n' + + '

フッターにキーボード ナビゲーション フォーカスがあり、表示可能なサイド バーがない場合、Shift+Tab を押すと、\n' + + ' フォーカスが最後ではなく最初のツール バー グループに移動します。

\n' + + '\n' + + '

UI セクション内の移動

\n' + + '\n' + + '

次の UI 要素に移動するには、適切な矢印キーを押します。

\n' + + '\n' + + '

左矢印右矢印のキー

\n' + + '\n' + + '
    \n' + + '
  • メニュー バーのメニュー間で移動します。
  • \n' + + '
  • メニュー内のサブメニューを開きます。
  • \n' + + '
  • ツール バー グループのボタン間で移動します。
  • \n' + + '
  • フッターの要素パスの項目間で移動します。
  • \n' + + '
\n' + + '\n' + + '

下矢印上矢印のキー

\n' + + '\n' + + '
    \n' + + '
  • メニュー内のメニュー項目間で移動します。
  • \n' + + '
  • ツール バー ポップアップ メニュー内のメニュー項目間で移動します。
  • \n' + + '
\n' + + '\n' + + '

矢印キーで、フォーカスされた UI セクション内で循環します。

\n' + + '\n' + + '

開いたメニュー、開いたサブメニュー、開いたポップアップ メニューを閉じるには、Esc キーを押します。

\n' + + '\n' + + '

現在のフォーカスが特定の UI セクションの「一番上」にある場合、Esc キーを押すと\n' + + ' キーボード ナビゲーションも完全に閉じられます。

\n' + + '\n' + + '

メニュー項目またはツール バー ボタンの実行

\n' + + '\n' + + '

目的のメニュー項目やツール バー ボタンが強調表示されている場合、リターンEnter、\n' + + ' またはスペース キーを押して項目を実行します。

\n' + + '\n' + + '

タブのないダイアログの移動

\n' + + '\n' + + '

タブのないダイアログでは、ダイアログが開くと最初の対話型コンポーネントがフォーカスされます。

\n' + + '\n' + + '

Tab または Shift+Tab を押して、対話型ダイアログ コンポーネント間で移動します。

\n' + + '\n' + + '

タブ付きダイアログの移動

\n' + + '\n' + + '

タブ付きダイアログでは、ダイアログが開くとタブ メニューの最初のボタンがフォーカスされます。

\n' + + '\n' + + '

Tab または\n' + + ' Shift+Tab を押して、このダイアログ タブの対話型コンポーネント間で移動します。

\n' + + '\n' + + '

タブ メニューをフォーカスしてから適切な矢印キーを押して表示可能なタブを循環して、\n' + + ' 別のダイアログに切り替えます。

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/kk.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/kk.js new file mode 100644 index 0000000..e31532f --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/kk.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.kk', +'

Пернетақта навигациясын бастау

\n' + + '\n' + + '
\n' + + '
Мәзір жолағын фокустау
\n' + + '
Windows немесе Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Құралдар тақтасын фокустау
\n' + + '
Windows немесе Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Төменгі деректемені фокустау
\n' + + '
Windows немесе Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Хабарландыруды белгілеу
\n' + + '
Windows немесе Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Мәтінмәндік құралдар тақтасын фокустау
\n' + + '
Windows, Linux немесе macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

Навигация бөлектелетін немесе Төменгі деректеме элементінің жолындағы бірінші элемент жағдайында асты сызылатын\n' + + ' бірінші ПИ элементінен басталады.

\n' + + '\n' + + '

ПИ бөлімдері арасында навигациялау

\n' + + '\n' + + '

Бір ПИ бөлімінен келесісіне өту үшін Tab пернесін басыңыз.

\n' + + '\n' + + '

Бір ПИ бөлімінен алдыңғысына өту үшін Shift+Tab пернесін басыңыз.

\n' + + '\n' + + '

Осы ПИ бөлімдерінің Tab реті:

\n' + + '\n' + + '
    \n' + + '
  1. Мәзір жолағы
  2. \n' + + '
  3. Әрбір құралдар тақтасы тобы
  4. \n' + + '
  5. Бүйірлік жолақ
  6. \n' + + '
  7. Төменгі деректемедегі элемент жолы
  8. \n' + + '
  9. Төменгі деректемедегі сөздер санын ауыстыру түймесі
  10. \n' + + '
  11. Төменгі деректемедегі брендингтік сілтеме
  12. \n' + + '
  13. Төменгі деректемедегі редактор өлшемін өзгерту тұтқасы
  14. \n' + + '
\n' + + '\n' + + '

ПИ бөлімі көрсетілмесе, ол өткізіп жіберіледі.

\n' + + '\n' + + '

Төменгі деректемеде пернетақта навигациясының фокусы болса және бүйірлік жолақ көрінбесе, Shift+Tab тіркесімін басу әрекеті\n' + + ' фокусты соңғысы емес, бірінші құралдар тақтасы тобына жылжытады.

\n' + + '\n' + + '

ПИ бөлімдерінде навигациялау

\n' + + '\n' + + '

Бір ПИ элементінен келесісіне өту үшін Arrow (Көрсеткі) пернесін басыңыз.

\n' + + '\n' + + '

Left (Сол жақ) және Right (Оң жақ) көрсеткі пернелері

\n' + + '\n' + + '
    \n' + + '
  • мәзір жолағындағы мәзірлер арасында жылжыту.
  • \n' + + '
  • мәзірде ішкі мәзірді ашу.
  • \n' + + '
  • құралдар тақтасы тобындағы түймелер арасында жылжыту.
  • \n' + + '
  • төменгі деректеме элементінің жолындағы элементтер арасында жылжыту.
  • \n' + + '
\n' + + '\n' + + '

Down (Төмен) және Up (Жоғары) көрсеткі пернелері

\n' + + '\n' + + '
    \n' + + '
  • мәзірдегі мәзір элементтері арасында жылжыту.
  • \n' + + '
  • құралдар тақтасының ашылмалы мәзіріндегі мәзір элементтері арасында жылжыту.
  • \n' + + '
\n' + + '\n' + + '

Фокусталған ПИ бөліміндегі Arrow (Көрсеткі) пернелерінің циклі.

\n' + + '\n' + + '

Ашық мәзірді жабу үшін ішкі мәзірді ашып немесе ашылмалы мәзірді ашып, Esc пернесін басыңыз.

\n' + + '\n' + + '

Ағымдағы фокус белгілі бір ПИ бөлімінің «үстінде» болса, Esc пернесін басу әрекеті пернетақта\n' + + ' навигациясын толығымен жабады.

\n' + + '\n' + + '

Мәзір элементін немесе құралдар тақтасы түймесін орындау

\n' + + '\n' + + '

Қажетті мәзір элементі немесе құралдар тақтасы түймесі бөлектелген кезде, элементті орындау үшін Return (Қайтару), Enter (Енгізу)\n' + + ' немесе Space bar (Бос орын) пернесін басыңыз.

\n' + + '\n' + + '

Белгіленбеген диалог терезелерін навигациялау

\n' + + '\n' + + '

Белгіленбеген диалог терезелерінде диалог терезесі ашылған кезде бірінші интерактивті құрамдас фокусталады.

\n' + + '\n' + + '

Tab немесе Shift+Tab пернесін басу арқылы интерактивті диалог терезесінің құрамдастары арасында навигациялаңыз.

\n' + + '\n' + + '

Белгіленген диалог терезелерін навигациялау

\n' + + '\n' + + '

Белгіленген диалог терезелерінде диалог терезесі ашылған кезде қойынды мәзіріндегі бірінші түйме фокусталады.

\n' + + '\n' + + '

Tab немесе\n' + + ' Shift+Tab пернесін басу арқылы осы диалог терезесі қойындысының интерактивті құрамдастары арасында навигациялаңыз.

\n' + + '\n' + + '

Қойынды мәзірінің фокусын беру арқылы басқа диалог терезесінің қойындысына ауысып, тиісті Arrow (Көрсеткі)\n' + + ' пернесін басу арқылы қолжетімді қойындылар арасында айналдыруға болады.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/ko_KR.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/ko_KR.js new file mode 100644 index 0000000..e7c8e7f --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/ko_KR.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.ko_KR', +'

키보드 탐색 시작

\n' + + '\n' + + '
\n' + + '
메뉴 모음 포커스 표시
\n' + + '
Windows 또는 Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
도구 모음 포커스 표시
\n' + + '
Windows 또는 Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
푸터 포커스 표시
\n' + + '
Windows 또는 Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
알림 포커스
\n' + + '
Windows 또는 Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
컨텍스트 도구 모음에 포커스 표시
\n' + + '
Windows, Linux 또는 macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

첫 번째 UI 항목에서 탐색이 시작되며, 이때 첫 번째 항목이 강조 표시되거나 푸터 요소 경로에 있는\n' + + ' 경우 밑줄 표시됩니다.

\n' + + '\n' + + '

UI 섹션 간 탐색

\n' + + '\n' + + '

한 UI 섹션에서 다음 UI 섹션으로 이동하려면 Tab(탭)을 누릅니다.

\n' + + '\n' + + '

한 UI 섹션에서 이전 UI 섹션으로 돌아가려면 Shift+Tab(시프트+탭)을 누릅니다.

\n' + + '\n' + + '

이 UI 섹션의 Tab(탭) 순서는 다음과 같습니다.

\n' + + '\n' + + '
    \n' + + '
  1. 메뉴 바
  2. \n' + + '
  3. 각 도구 모음 그룹
  4. \n' + + '
  5. 사이드바
  6. \n' + + '
  7. 푸터의 요소 경로
  8. \n' + + '
  9. 푸터의 단어 수 토글 버튼
  10. \n' + + '
  11. 푸터의 브랜딩 링크
  12. \n' + + '
  13. 푸터의 에디터 크기 변경 핸들
  14. \n' + + '
\n' + + '\n' + + '

UI 섹션이 없는 경우 건너뛰기합니다.

\n' + + '\n' + + '

푸터에 키보드 탐색 포커스가 있고 사이드바는 보이지 않는 경우 Shift+Tab(시프트+탭)을 누르면\n' + + ' 포커스 표시가 마지막이 아닌 첫 번째 도구 모음 그룹으로 이동합니다.

\n' + + '\n' + + '

UI 섹션 내 탐색

\n' + + '\n' + + '

한 UI 요소에서 다음 UI 요소로 이동하려면 적절한 화살표 키를 누릅니다.

\n' + + '\n' + + '

왼쪽오른쪽 화살표 키의 용도:

\n' + + '\n' + + '
    \n' + + '
  • 메뉴 모음에서 메뉴 항목 사이를 이동합니다.
  • \n' + + '
  • 메뉴에서 하위 메뉴를 엽니다.
  • \n' + + '
  • 도구 모음 그룹에서 버튼 사이를 이동합니다.
  • \n' + + '
  • 푸터의 요소 경로에서 항목 간에 이동합니다.
  • \n' + + '
\n' + + '\n' + + '

아래 화살표 키의 용도:

\n' + + '\n' + + '
    \n' + + '
  • 메뉴에서 메뉴 항목 사이를 이동합니다.
  • \n' + + '
  • 도구 모음 팝업 메뉴에서 메뉴 항목 사이를 이동합니다.
  • \n' + + '
\n' + + '\n' + + '

화살표 키는 포커스 표시 UI 섹션 내에서 순환됩니다.

\n' + + '\n' + + '

열려 있는 메뉴, 열려 있는 하위 메뉴 또는 열려 있는 팝업 메뉴를 닫으려면 Esc 키를 누릅니다.

\n' + + '\n' + + "

현재 포커스 표시가 특정 UI 섹션 '상단'에 있는 경우 이때도 Esc 키를 누르면\n" + + ' 키보드 탐색이 완전히 종료됩니다.

\n' + + '\n' + + '

메뉴 항목 또는 도구 모음 버튼 실행

\n' + + '\n' + + '

원하는 메뉴 항목 또는 도구 모음 버튼이 강조 표시되어 있을 때 Return(리턴), Enter(엔터),\n' + + ' 또는 Space bar(스페이스바)를 눌러 해당 항목을 실행합니다.

\n' + + '\n' + + '

탭이 없는 대화 탐색

\n' + + '\n' + + '

탭이 없는 대화의 경우, 첫 번째 대화형 요소가 포커스 표시된 상태로 대화가 열립니다.

\n' + + '\n' + + '

대화형 요소들 사이를 이동할 때는 Tab(탭) 또는 Shift+Tab(시프트+탭)을 누릅니다.

\n' + + '\n' + + '

탭이 있는 대화 탐색

\n' + + '\n' + + '

탭이 있는 대화의 경우, 탭 메뉴에서 첫 번째 버튼이 포커스 표시된 상태로 대화가 열립니다.

\n' + + '\n' + + '

이 대화 탭의 대화형 요소들 사이를 이동할 때는 Tab(탭) 또는\n' + + ' Shift+Tab(시프트+탭)을 누릅니다.

\n' + + '\n' + + '

다른 대화 탭으로 이동하려면 탭 메뉴를 포커스 표시한 다음 적절한 화살표\n' + + ' 키를 눌러 사용 가능한 탭들을 지나 원하는 탭으로 이동합니다.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/ms.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/ms.js new file mode 100644 index 0000000..2c047bb --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/ms.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.ms', +'

Mulakan navigasi papan kekunci

\n' + + '\n' + + '
\n' + + '
Fokus bar Menu
\n' + + '
Windows atau Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Fokus Bar Alat
\n' + + '
Windows atau Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Fokus pengaki
\n' + + '
Windows atau Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Tumpu kepada pemberitahuan
\n' + + '
Windows atau Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Fokus bar alat kontekstual
\n' + + '
Windows, Linux atau macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

Navigasi akan bermula pada item UI pertama, yang akan diserlahkan atau digaris bawah dalam saiz item pertama dalam\n' + + ' laluan elemen Pengaki.

\n' + + '\n' + + '

Navigasi antara bahagian UI

\n' + + '\n' + + '

Untuk bergerak dari satu bahagian UI ke yang seterusnya, tekan Tab.

\n' + + '\n' + + '

Untuk bergerak dari satu bahagian UI ke yang sebelumnya, tekan Shift+Tab.

\n' + + '\n' + + '

Tertib Tab bahagian UI ini ialah:

\n' + + '\n' + + '
    \n' + + '
  1. Bar menu
  2. \n' + + '
  3. Setiap kumpulan bar alat
  4. \n' + + '
  5. Bar sisi
  6. \n' + + '
  7. Laluan elemen dalam pengaki
  8. \n' + + '
  9. Butang togol kiraan perkataan dalam pengaki
  10. \n' + + '
  11. Pautan penjenamaan dalam pengaki
  12. \n' + + '
  13. Pemegang saiz semula editor dalam pengaki
  14. \n' + + '
\n' + + '\n' + + '

Jika bahagian UI tidak wujud, ia dilangkau.

\n' + + '\n' + + '

Jika pengaki mempunyai fokus navigasi papan kekunci dan tiada bar sisi kelihatan, menekan Shift+Tab\n' + + ' akan mengalihkan fokus ke kumpulan bar alat pertama, bukannya yang terakhir.

\n' + + '\n' + + '

Navigasi dalam bahagian UI

\n' + + '\n' + + '

Untuk bergerak dari satu elemen UI ke yang seterusnya, tekan kekunci Anak Panah yang bersesuaian.

\n' + + '\n' + + '

Kekunci anak panah Kiri dan Kanan

\n' + + '\n' + + '
    \n' + + '
  • bergerak antara menu dalam bar menu.
  • \n' + + '
  • membukan submenu dalam menu.
  • \n' + + '
  • bergerak antara butang dalam kumpulan bar alat.
  • \n' + + '
  • Laluan elemen dalam pengaki.
  • \n' + + '
\n' + + '\n' + + '

Kekunci anak panah Bawah dan Atas

\n' + + '\n' + + '
    \n' + + '
  • bergerak antara item menu dalam menu.
  • \n' + + '
  • bergerak antara item dalam menu timbul bar alat.
  • \n' + + '
\n' + + '\n' + + '

Kekunci Anak Panah berkitar dalam bahagian UI difokuskan.

\n' + + '\n' + + '

Untuk menutup menu buka, submenu terbuka atau menu timbul terbuka, tekan kekunci Esc.

\n' + + '\n' + + "

Jika fokus semasa berada di bahagian 'atas' bahagian UI tertentu, menekan kekunci Esc juga akan keluar daripada\n" + + ' navigasi papan kekunci sepenuhnya.

\n' + + '\n' + + '

Laksanakan item menu atau butang bar alat

\n' + + '\n' + + '

Apabila item menu atau butang bar alat yang diinginkan diserlahkan, tekan Return, Enter,\n' + + ' atau bar Space untuk melaksanakan item.

\n' + + '\n' + + '

Navigasi ke dialog tidak bertab

\n' + + '\n' + + '

Dalam dialog tidak bertab, komponen interaksi pertama difokuskan apabila dialog dibuka.

\n' + + '\n' + + '

Navigasi antara komponen dialog interaktif dengan menekan Tab atau Shift+Tab.

\n' + + '\n' + + '

Navigasi ke dialog bertab

\n' + + '\n' + + '

Dalam dialog bertab, butang pertama dalam menu tab difokuskan apabila dialog dibuka.

\n' + + '\n' + + '

Navigasi antara komponen interaktif tab dialog ini dengan menekan Tab atau\n' + + ' Shift+Tab.

\n' + + '\n' + + '

Tukar kepada tab dialog lain dengan memfokuskan menu tab, kemudian menekan kekunci Anak Panah yang bersesuaian\n' + + ' untuk berkitar menerusi tab yang tersedia.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/nb_NO.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/nb_NO.js new file mode 100644 index 0000000..071e3f5 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/nb_NO.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.nb_NO', +'

Starte tastaturnavigering

\n' + + '\n' + + '
\n' + + '
Utheve menylinjen
\n' + + '
Windows eller Linux: Alt + F9
\n' + + '
macOS: ⌥F9
\n' + + '
Utheve verktøylinjen
\n' + + '
Windows eller Linux: Alt + F10
\n' + + '
macOS: ⌥F10
\n' + + '
Utheve bunnteksten
\n' + + '
Windows eller Linux: Alt + F11
\n' + + '
macOS: ⌥F11
\n' + + '
Fokuser på varselet
\n' + + '
Windows eller Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Utheve en kontekstuell verktøylinje
\n' + + '
Windows, Linux eller macOS: Ctrl + F9
\n' + + '
\n' + + '\n' + + '

Navigeringen starter ved det første grensesnittelementet, som utheves, eller understrekes når det gjelder det første elementet i\n' + + ' elementstien i bunnteksten.

\n' + + '\n' + + '

Navigere mellom grensesnittdeler

\n' + + '\n' + + '

Du kan bevege deg fra én grensesnittdel til den neste ved å trykke på tabulatortasten.

\n' + + '\n' + + '

Du kan bevege deg fra én grensesnittdel til den forrige ved å trykke på Shift + tabulatortasten.

\n' + + '\n' + + '

Rekkefølgen til tabulatortasten gjennom grensesnittdelene er:

\n' + + '\n' + + '
    \n' + + '
  1. Menylinjen
  2. \n' + + '
  3. Hver gruppe på verktøylinjen
  4. \n' + + '
  5. Sidestolpen
  6. \n' + + '
  7. Elementstien i bunnteksten
  8. \n' + + '
  9. Veksleknappen for ordantall i bunnteksten
  10. \n' + + '
  11. Merkelenken i bunnteksten
  12. \n' + + '
  13. Skaleringshåndtaket for redigeringsprogrammet i bunnteksten
  14. \n' + + '
\n' + + '\n' + + '

Hvis en grensesnittdel ikke er til stede, blir den hoppet over.

\n' + + '\n' + + '

Hvis tastaturnavigeringen har uthevet bunnteksten og det ikke finnes en synlig sidestolpe, kan du trykke på Shift + tabulatortasten\n' + + ' for å flytte fokuset til den første gruppen på verktøylinjen i stedet for den siste.

\n' + + '\n' + + '

Navigere innenfor grensesnittdeler

\n' + + '\n' + + '

Du kan bevege deg fra ett grensesnittelement til det neste ved å trykke på den aktuelle piltasten.

\n' + + '\n' + + '

De venstre og høyre piltastene

\n' + + '\n' + + '
    \n' + + '
  • beveger deg mellom menyer på menylinjen.
  • \n' + + '
  • åpner en undermeny i en meny.
  • \n' + + '
  • beveger deg mellom knapper i en gruppe på verktøylinjen.
  • \n' + + '
  • beveger deg mellom elementer i elementstien i bunnteksten.
  • \n' + + '
\n' + + '\n' + + '

Ned- og opp-piltastene

\n' + + '\n' + + '
    \n' + + '
  • beveger deg mellom menyelementer i en meny.
  • \n' + + '
  • beveger deg mellom elementer i en hurtigmeny på verktøylinjen.
  • \n' + + '
\n' + + '\n' + + '

Med piltastene kan du bevege deg innenfor den uthevede grensesnittdelen.

\n' + + '\n' + + '

Du kan lukke en åpen meny, en åpen undermeny eller en åpen hurtigmeny ved å klikke på Esc-tasten.

\n' + + '\n' + + '

Hvis det øverste nivået i en grensesnittdel er uthevet, kan du ved å trykke på Esc også avslutte\n' + + ' tastaturnavigeringen helt.

\n' + + '\n' + + '

Utføre et menyelement eller en knapp på en verktøylinje

\n' + + '\n' + + '

Når det ønskede menyelementet eller verktøylinjeknappen er uthevet, trykker du på Retur, Enter,\n' + + ' eller mellomromstasten for å utføre elementet.

\n' + + '\n' + + '

Navigere i dialogbokser uten faner

\n' + + '\n' + + '

I dialogbokser uten faner blir den første interaktive komponenten uthevet når dialogboksen åpnes.

\n' + + '\n' + + '

Naviger mellom interaktive komponenter i dialogboksen ved å trykke på tabulatortasten eller Shift + tabulatortasten.

\n' + + '\n' + + '

Navigere i fanebaserte dialogbokser

\n' + + '\n' + + '

I fanebaserte dialogbokser blir den første knappen i fanemenyen uthevet når dialogboksen åpnes.

\n' + + '\n' + + '

Naviger mellom interaktive komponenter i fanen ved å trykke på tabulatortasten eller\n' + + ' Shift + tabulatortasten.

\n' + + '\n' + + '

Veksle til en annen fane i dialogboksen ved å utheve fanemenyen, og trykk deretter på den aktuelle piltasten\n' + + ' for å bevege deg mellom de tilgjengelige fanene.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/nl.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/nl.js new file mode 100644 index 0000000..05c07ae --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/nl.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.nl', +'

Toetsenbordnavigatie starten

\n' + + '\n' + + '
\n' + + '
Focus op de menubalk instellen
\n' + + '
Windows of Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Focus op de werkbalk instellen
\n' + + '
Windows of Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Focus op de voettekst instellen
\n' + + '
Windows of Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Focus op de melding instellen
\n' + + '
Windows of Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Focus op een contextuele werkbalk instellen
\n' + + '
Windows, Linux of macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

De navigatie start bij het eerste UI-item, dat wordt gemarkeerd of onderstreept als het eerste item zich in\n' + + ' in het elementenpad van de voettekst bevindt.

\n' + + '\n' + + '

Navigeren tussen UI-secties

\n' + + '\n' + + '

Druk op Tab om naar de volgende UI-sectie te gaan.

\n' + + '\n' + + '

Druk op Shift+Tab om naar de vorige UI-sectie te gaan.

\n' + + '\n' + + '

De Tab-volgorde van deze UI-secties is:

\n' + + '\n' + + '
    \n' + + '
  1. Menubalk
  2. \n' + + '
  3. Elke werkbalkgroep
  4. \n' + + '
  5. Zijbalk
  6. \n' + + '
  7. Elementenpad in de voettekst
  8. \n' + + '
  9. Wisselknop voor aantal woorden in de voettekst
  10. \n' + + '
  11. Merkkoppeling in de voettekst
  12. \n' + + '
  13. Greep voor het wijzigen van het formaat van de editor in de voettekst
  14. \n' + + '
\n' + + '\n' + + '

Als een UI-sectie niet aanwezig is, wordt deze overgeslagen.

\n' + + '\n' + + '

Als de focus van de toetsenbordnavigatie is ingesteld op de voettekst en er geen zichtbare zijbalk is, kun je op Shift+Tab drukken\n' + + ' om de focus naar de eerste werkbalkgroep in plaats van de laatste te verplaatsen.

\n' + + '\n' + + '

Navigeren binnen UI-secties

\n' + + '\n' + + '

Druk op de pijltjestoets om naar het betreffende UI-element te gaan.

\n' + + '\n' + + '

Met de pijltjestoetsen Links en Rechts

\n' + + '\n' + + '
    \n' + + "
  • wissel je tussen menu's in de menubalk.
  • \n" + + '
  • open je een submenu in een menu.
  • \n' + + '
  • wissel je tussen knoppen in een werkbalkgroep.
  • \n' + + '
  • wissel je tussen items in het elementenpad in de voettekst.
  • \n' + + '
\n' + + '\n' + + '

Met de pijltjestoetsen Omlaag en Omhoog

\n' + + '\n' + + '
    \n' + + '
  • wissel je tussen menu-items in een menu.
  • \n' + + '
  • wissel je tussen items in een werkbalkpop-upmenu.
  • \n' + + '
\n' + + '\n' + + '

Met de pijltjestoetsen wissel je binnen de UI-sectie waarop de focus is ingesteld.

\n' + + '\n' + + '

Druk op de toets Esc om een geopend menu, submenu of pop-upmenu te sluiten.

\n' + + '\n' + + "

Als de huidige focus is ingesteld 'bovenaan' een bepaalde UI-sectie, kun je op de toets Esc drukken\n" + + ' om de toetsenbordnavigatie af te sluiten.

\n' + + '\n' + + '

Een menu-item of werkbalkknop uitvoeren

\n' + + '\n' + + '

Als het gewenste menu-item of de gewenste werkbalkknop is gemarkeerd, kun je op Return, Enter\n' + + ' of de spatiebalk drukken om het item uit te voeren.

\n' + + '\n' + + '

Navigeren in dialoogvensters zonder tabblad

\n' + + '\n' + + '

Als een dialoogvenster zonder tabblad wordt geopend, wordt de focus ingesteld op het eerste interactieve onderdeel.

\n' + + '\n' + + '

Je kunt navigeren tussen interactieve onderdelen van een dialoogvenster door op Tab of Shift+Tab te drukken.

\n' + + '\n' + + '

Navigeren in dialoogvensters met tabblad

\n' + + '\n' + + '

Als een dialoogvenster met tabblad wordt geopend, wordt de focus ingesteld op de eerste knop in het tabbladmenu.

\n' + + '\n' + + '

Je kunt navigeren tussen interactieve onderdelen van dit tabblad van het dialoogvenster door op Tab of\n' + + ' Shift+Tab te drukken.

\n' + + '\n' + + '

Je kunt overschakelen naar een ander tabblad van het dialoogvenster door de focus in te stellen op het tabbladmenu en vervolgens op de juiste pijltjestoets\n' + + ' te drukken om tussen de beschikbare tabbladen te wisselen.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/pl.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/pl.js new file mode 100644 index 0000000..e89f808 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/pl.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.pl', +'

Początek nawigacji przy użyciu klawiatury

\n' + + '\n' + + '
\n' + + '
Ustaw fokus na pasek menu
\n' + + '
Windows lub Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Ustaw fokus na pasek narzędzi
\n' + + '
Windows lub Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Ustaw fokus na sekcję Footer
\n' + + '
Windows lub Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Skup się na powiadomieniu
\n' + + '
Windows lub Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Ustaw fokus na kontekstowy pasek narzędzi
\n' + + '
Windows, Linux lub macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

Nawigacja zostanie rozpoczęta od pierwszego elementu interfejsu użytkownika, który jest podświetlony lub — w przypadku pierwszego elementu\n' + + ' w ścieżce elementów w sekcji Footer — podkreślony.

\n' + + '\n' + + '

Nawigacja pomiędzy sekcjami interfejsu użytkownika

\n' + + '\n' + + '

Aby przenieść się z danej sekcji interfejsu użytkownika do następnej, naciśnij Tab.

\n' + + '\n' + + '

Aby przenieść się z danej sekcji interfejsu użytkownika do poprzedniej, naciśnij Shift+Tab.

\n' + + '\n' + + '

Kolejność klawisza Tab w takich sekcjach interfejsu użytkownika jest następująca:

\n' + + '\n' + + '
    \n' + + '
  1. Pasek menu
  2. \n' + + '
  3. Każda grupa na pasku narzędzi
  4. \n' + + '
  5. Pasek boczny
  6. \n' + + '
  7. Ścieżka elementów w sekcji Footer
  8. \n' + + '
  9. Przycisk przełączania liczby słów w sekcji Footer
  10. \n' + + '
  11. Łącze brandujące w sekcji Footer
  12. \n' + + '
  13. Uchwyt zmiany rozmiaru edytora w sekcji Footer
  14. \n' + + '
\n' + + '\n' + + '

Jeżeli nie ma sekcji interfejsu użytkownika, jest to pomijane.

\n' + + '\n' + + '

Jeżeli na sekcji Footer jest ustawiony fokus nawigacji przy użyciu klawiatury i nie ma widocznego paska bocznego, naciśnięcie Shift+Tab\n' + + ' przenosi fokus na pierwszą grupę paska narzędzi, a nie na ostatnią.

\n' + + '\n' + + '

Nawigacja wewnątrz sekcji interfejsu użytkownika

\n' + + '\n' + + '

Aby przenieść się z danego elementu interfejsu użytkownika do następnego, naciśnij odpowiedni klawisz strzałki.

\n' + + '\n' + + '

Klawisze strzałek w prawo i w lewo służą do

\n' + + '\n' + + '
    \n' + + '
  • przenoszenia się pomiędzy menu na pasku menu,
  • \n' + + '
  • otwarcia podmenu w menu,
  • \n' + + '
  • przenoszenia się pomiędzy przyciskami w grupie paska narzędzi,
  • \n' + + '
  • przenoszenia się pomiędzy elementami w ścieżce elementów w sekcji Footer.
  • \n' + + '
\n' + + '\n' + + '

Klawisze strzałek w dół i w górę służą do

\n' + + '\n' + + '
    \n' + + '
  • przenoszenia się pomiędzy elementami menu w menu,
  • \n' + + '
  • przenoszenia się pomiędzy elementami w wyskakującym menu paska narzędzi.
  • \n' + + '
\n' + + '\n' + + '

Klawisze strzałek służą do przemieszczania się w sekcji interfejsu użytkownika z ustawionym fokusem.

\n' + + '\n' + + '

Aby zamknąć otwarte menu, otwarte podmenu lub otwarte menu wyskakujące, naciśnij klawisz Esc.

\n' + + '\n' + + '

Jeżeli fokus jest ustawiony na górze konkretnej sekcji interfejsu użytkownika, naciśnięcie klawisza Esc powoduje wyjście\n' + + ' z nawigacji przy użyciu klawiatury.

\n' + + '\n' + + '

Wykonanie elementu menu lub przycisku paska narzędzi

\n' + + '\n' + + '

Gdy podświetlony jest żądany element menu lub przycisk paska narzędzi, naciśnij klawisz Return, Enter\n' + + ' lub Spacja, aby go wykonać.

\n' + + '\n' + + '

Nawigacja po oknie dialogowym bez kart

\n' + + '\n' + + '

Gdy otwiera się okno dialogowe bez kart, fokus ustawiany jest na pierwszą interaktywną część okna.

\n' + + '\n' + + '

Pomiędzy interaktywnymi częściami okna dialogowego nawiguj, naciskając klawisze Tab lub Shift+Tab.

\n' + + '\n' + + '

Nawigacja po oknie dialogowym z kartami

\n' + + '\n' + + '

W przypadku okna dialogowego z kartami po otwarciu okna dialogowego fokus ustawiany jest na pierwszy przycisk w menu karty.

\n' + + '\n' + + '

Nawigację pomiędzy interaktywnymi częściami karty okna dialogowego prowadzi się poprzez naciskanie klawiszy Tab lub\n' + + ' Shift+Tab.

\n' + + '\n' + + '

Przełączenie się na inną kartę okna dialogowego wykonuje się poprzez ustawienie fokusu na menu karty i naciśnięcie odpowiedniego klawisza strzałki\n' + + ' w celu przemieszczenia się pomiędzy dostępnymi kartami.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/pt_BR.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/pt_BR.js new file mode 100644 index 0000000..2938fcf --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/pt_BR.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.pt_BR', +'

Iniciar navegação pelo teclado

\n' + + '\n' + + '
\n' + + '
Foco na barra de menus
\n' + + '
Windows ou Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Foco na barra de ferramentas
\n' + + '
Windows ou Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Foco no rodapé
\n' + + '
Windows ou Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Foco na notificação
\n' + + '
Windows ou Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Foco na barra de ferramentas contextual
\n' + + '
Windows, Linux ou macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

A navegação inicia no primeiro item da IU, que será destacado ou sublinhado no caso do primeiro item no\n' + + ' caminho do elemento Rodapé.

\n' + + '\n' + + '

Navegar entre seções da IU

\n' + + '\n' + + '

Para ir de uma seção da IU para a seguinte, pressione Tab.

\n' + + '\n' + + '

Para ir de uma seção da IU para a anterior, pressione Shift+Tab.

\n' + + '\n' + + '

A ordem de Tab destas seções da IU é:

\n' + + '\n' + + '
    \n' + + '
  1. Barra de menus
  2. \n' + + '
  3. Cada grupo da barra de ferramentas
  4. \n' + + '
  5. Barra lateral
  6. \n' + + '
  7. Caminho do elemento no rodapé
  8. \n' + + '
  9. Botão de alternar contagem de palavras no rodapé
  10. \n' + + '
  11. Link da marca no rodapé
  12. \n' + + '
  13. Alça de redimensionamento do editor no rodapé
  14. \n' + + '
\n' + + '\n' + + '

Se não houver uma seção da IU, ela será pulada.

\n' + + '\n' + + '

Se o rodapé tiver o foco da navegação pelo teclado e não houver uma barra lateral visível, pressionar Shift+Tab\n' + + ' move o foco para o primeiro grupo da barra de ferramentas, não para o último.

\n' + + '\n' + + '

Navegar dentro das seções da IU

\n' + + '\n' + + '

Para ir de um elemento da IU para o seguinte, pressione a Seta correspondente.

\n' + + '\n' + + '

As teclas de seta Esquerda e Direita

\n' + + '\n' + + '
    \n' + + '
  • movem entre menus na barra de menus.
  • \n' + + '
  • abrem um submenu em um menu.
  • \n' + + '
  • movem entre botões em um grupo da barra de ferramentas.
  • \n' + + '
  • movem entre itens no caminho do elemento do rodapé.
  • \n' + + '
\n' + + '\n' + + '

As teclas de seta Abaixo e Acima

\n' + + '\n' + + '
    \n' + + '
  • movem entre itens de menu em um menu.
  • \n' + + '
  • movem entre itens em um menu suspenso da barra de ferramentas.
  • \n' + + '
\n' + + '\n' + + '

As teclas de Seta alternam dentre a seção da IU em foco.

\n' + + '\n' + + '

Para fechar um menu aberto, um submenu aberto ou um menu suspenso aberto, pressione Esc.

\n' + + '\n' + + '

Se o foco atual estiver no ‘alto’ de determinada seção da IU, pressionar Esc também sai\n' + + ' totalmente da navegação pelo teclado.

\n' + + '\n' + + '

Executar um item de menu ou botão da barra de ferramentas

\n' + + '\n' + + '

Com o item de menu ou botão da barra de ferramentas desejado destacado, pressione Return, Enter,\n' + + ' ou a Barra de espaço para executar o item.

\n' + + '\n' + + '

Navegar por caixas de diálogo sem guias

\n' + + '\n' + + '

Em caixas de diálogo sem guias, o primeiro componente interativo recebe o foco quando a caixa de diálogo abre.

\n' + + '\n' + + '

Navegue entre componentes interativos de caixa de diálogo pressionando Tab ou Shift+Tab.

\n' + + '\n' + + '

Navegar por caixas de diálogo com guias

\n' + + '\n' + + '

Em caixas de diálogo com guias, o primeiro botão no menu da guia recebe o foco quando a caixa de diálogo abre.

\n' + + '\n' + + '

Navegue entre componentes interativos dessa guia da caixa de diálogo pressionando Tab ou\n' + + ' Shift+Tab.

\n' + + '\n' + + '

Alterne para outra guia da caixa de diálogo colocando o foco no menu da guia e pressionando a Seta\n' + + ' adequada para percorrer as guias disponíveis.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/pt_PT.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/pt_PT.js new file mode 100644 index 0000000..03da3d6 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/pt_PT.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.pt_PT', +'

Iniciar navegação com teclado

\n' + + '\n' + + '
\n' + + '
Foco na barra de menu
\n' + + '
Windows ou Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Foco na barra de ferramentas
\n' + + '
Windows ou Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Foco no rodapé
\n' + + '
Windows ou Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Focar a notificação
\n' + + '
Windows ou Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Foco numa barra de ferramentas contextual
\n' + + '
Windows, Linux ou macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

A navegação começará no primeiro item de IU, que estará realçado ou sublinhado, no caso do primeiro item no\n' + + ' caminho do elemento do rodapé.

\n' + + '\n' + + '

Navegar entre secções de IU

\n' + + '\n' + + '

Para se mover de uma secção de IU para a seguinte, prima Tab.

\n' + + '\n' + + '

Para se mover de uma secção de IU para a anterior, prima Shift+Tab.

\n' + + '\n' + + '

A ordem de tabulação destas secções de IU é:

\n' + + '\n' + + '
    \n' + + '
  1. Barra de menu
  2. \n' + + '
  3. Cada grupo da barra de ferramentas
  4. \n' + + '
  5. Barra lateral
  6. \n' + + '
  7. Caminho do elemento no rodapé
  8. \n' + + '
  9. Botão de alternar da contagem de palavras no rodapé
  10. \n' + + '
  11. Ligação da marca no rodapé
  12. \n' + + '
  13. Alça de redimensionamento do editor no rodapé
  14. \n' + + '
\n' + + '\n' + + '

Se uma secção de IU não estiver presente, é ignorada.

\n' + + '\n' + + '

Se o rodapé tiver foco de navegação com teclado e não existir uma barra lateral visível, premir Shift+Tab\n' + + ' move o foco para o primeiro grupo da barra de ferramentas e não para o último.

\n' + + '\n' + + '

Navegar nas secções de IU

\n' + + '\n' + + '

Para se mover de um elemento de IU para o seguinte, prima a tecla de seta adequada.

\n' + + '\n' + + '

As teclas de seta Para a esquerda e Para a direita

\n' + + '\n' + + '
    \n' + + '
  • movem-se entre menus na barra de menu.
  • \n' + + '
  • abrem um submenu num menu.
  • \n' + + '
  • movem-se entre botões num grupo da barra de ferramentas.
  • \n' + + '
  • movem-se entre itens no caminho do elemento do rodapé.
  • \n' + + '
\n' + + '\n' + + '

As teclas de seta Para cima e Para baixo

\n' + + '\n' + + '
    \n' + + '
  • movem-se entre itens de menu num menu.
  • \n' + + '
  • movem-se entre itens num menu de pop-up da barra de ferramentas.
  • \n' + + '
\n' + + '\n' + + '

As teclas de seta deslocam-se ciclicamente na secção de IU em foco.

\n' + + '\n' + + '

Para fechar um menu aberto, um submenu aberto ou um menu de pop-up aberto, prima a tecla Esc.

\n' + + '\n' + + '

Se o foco atual estiver no "topo" de determinada secção de IU, premir a tecla Esc também fecha\n' + + ' completamente a navegação com teclado.

\n' + + '\n' + + '

Executar um item de menu ou botão da barra de ferramentas

\n' + + '\n' + + '

Quando o item de menu ou o botão da barra de ferramentas pretendido estiver realçado, prima Retrocesso, Enter\n' + + ' ou a Barra de espaço para executar o item.

\n' + + '\n' + + '

Navegar em diálogos sem separadores

\n' + + '\n' + + '

Nos diálogos sem separadores, o primeiro componente interativo fica em foco quando o diálogo abre.

\n' + + '\n' + + '

Navegue entre componentes interativos do diálogo, premindo Tab ou Shift+Tab.

\n' + + '\n' + + '

Navegar em diálogos com separadores

\n' + + '\n' + + '

Nos diálogos com separadores, o primeiro botão no menu do separador fica em foco quando o diálogo abre.

\n' + + '\n' + + '

Navegue entre os componentes interativos deste separador do diálogo, premindo Tab ou\n' + + ' Shift+Tab.

\n' + + '\n' + + '

Mude para outro separador do diálogo colocando o menu do separador em foco e, em seguida, premindo a tecla de seta\n' + + ' adequada para se deslocar ciclicamente pelos separadores disponíveis.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/ro.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/ro.js new file mode 100644 index 0000000..38d3441 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/ro.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.ro', +'

Începeți navigarea de la tastatură

\n' + + '\n' + + '
\n' + + '
Focalizare pe bara de meniu
\n' + + '
Windows sau Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Focalizare pe bara de instrumente
\n' + + '
Windows sau Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Focalizare pe subsol
\n' + + '
Windows sau Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Focalizare pe notificare
\n' + + '
Windows sau Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Focalizare pe o bară de instrumente contextuală
\n' + + '
Windows, Linux sau macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

Navigarea va începe de la primul element al interfeței cu utilizatorul, care va fi evidențiat sau subliniat în cazul primului element din\n' + + ' calea elementului Subsol.

\n' + + '\n' + + '

Navigați între secțiunile interfeței cu utilizatorul

\n' + + '\n' + + '

Pentru a trece de la o secțiune a interfeței cu utilizatorul la alta, apăsați Tab.

\n' + + '\n' + + '

Pentru a trece de la o secțiune a interfeței cu utilizatorul la cea anterioară, apăsați Shift+Tab.

\n' + + '\n' + + '

Ordinea cu Tab a acestor secțiuni ale interfeței cu utilizatorul este următoarea:

\n' + + '\n' + + '
    \n' + + '
  1. Bara de meniu
  2. \n' + + '
  3. Fiecare grup de bare de instrumente
  4. \n' + + '
  5. Bara laterală
  6. \n' + + '
  7. Calea elementului în subsol
  8. \n' + + '
  9. Buton de comutare a numărului de cuvinte în subsol
  10. \n' + + '
  11. Link de branding în subsol
  12. \n' + + '
  13. Mâner de redimensionare a editorului în subsol
  14. \n' + + '
\n' + + '\n' + + '

În cazul în care o secțiune a interfeței cu utilizatorul nu este prezentă, aceasta este omisă.

\n' + + '\n' + + '

În cazul în care subsolul are focalizarea navigației asupra tastaturii și nu există o bară laterală vizibilă, apăsarea butonului Shift+Tab\n' + + ' mută focalizarea pe primul grup de bare de instrumente, nu pe ultimul.

\n' + + '\n' + + '

Navigați în secțiunile interfeței cu utilizatorul

\n' + + '\n' + + '

Pentru a trece de la un element de interfață cu utilizatorul la următorul, apăsați tasta cu săgeata corespunzătoare.

\n' + + '\n' + + '

Tastele cu săgeți către stânga și dreapta

\n' + + '\n' + + '
    \n' + + '
  • navighează între meniurile din bara de meniuri.
  • \n' + + '
  • deschid un sub-meniu dintr-un meniu.
  • \n' + + '
  • navighează între butoanele dintr-un grup de bare de instrumente.
  • \n' + + '
  • navighează între elementele din calea elementelor subsolului.
  • \n' + + '
\n' + + '\n' + + '

Tastele cu săgeți în sus și în jos

\n' + + '\n' + + '
    \n' + + '
  • navighează între elementele de meniu dintr-un meniu.
  • \n' + + '
  • navighează între elementele unui meniu pop-up din bara de instrumente.
  • \n' + + '
\n' + + '\n' + + '

Tastele cu săgeți navighează în cadrul secțiunii interfeței cu utilizatorul asupra căreia se focalizează.

\n' + + '\n' + + '

Pentru a închide un meniu deschis, un sub-meniu deschis sau un meniu pop-up deschis, apăsați tasta Esc.

\n' + + '\n' + + '

Dacă focalizarea curentă este asupra „părții superioare” a unei anumite secțiuni a interfeței cu utilizatorul, prin apăsarea tastei Esc se iese, de asemenea,\n' + + ' în întregime din navigarea de la tastatură.

\n' + + '\n' + + '

Executarea unui element de meniu sau a unui buton din bara de instrumente

\n' + + '\n' + + '

Atunci când elementul de meniu dorit sau butonul dorit din bara de instrumente este evidențiat, apăsați Return, Enter,\n' + + ' sau bara de spațiu pentru a executa elementul.

\n' + + '\n' + + '

Navigarea de dialoguri fără file

\n' + + '\n' + + '

În dialogurile fără file, prima componentă interactivă beneficiază de focalizare la deschiderea dialogului.

\n' + + '\n' + + '

Navigați între componentele dialogului interactiv apăsând Tab sau Shift+Tab.

\n' + + '\n' + + '

Navigarea de dialoguri cu file

\n' + + '\n' + + '

În dialogurile cu file, primul buton din meniul cu file beneficiază de focalizare la deschiderea dialogului.

\n' + + '\n' + + '

Navigați între componentele interactive ale acestei file de dialog apăsând Tab sau\n' + + ' Shift+Tab.

\n' + + '\n' + + '

Treceți la o altă filă de dialog focalizând asupra meniului cu file și apoi apăsând săgeata corespunzătoare\n' + + ' pentru a parcurge filele disponibile.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/ru.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/ru.js new file mode 100644 index 0000000..d310f54 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/ru.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.ru', +'

Начните управление с помощью клавиатуры

\n' + + '\n' + + '
\n' + + '
Фокус на панели меню
\n' + + '
Windows или Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Фокус на панели инструментов
\n' + + '
Windows или Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Фокус на нижнем колонтитуле
\n' + + '
Windows или Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Фокус на уведомлении
\n' + + '
Windows или Linux: Alt+12
\n' + + '
macOS: ⌥F12
\n' + + '
Фокус на контекстной панели инструментов
\n' + + '
Windows, Linux или macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

Первый доступный для управления элемент интерфейса будет выделен цветом или подчеркнут (если он находится\n' + + ' в пути элементов нижнего колонтитула).

\n' + + '\n' + + '

Переход между разделами пользовательского интерфейса

\n' + + '\n' + + '

Чтобы перейти из текущего раздела интерфейса в следующий, нажмите Tab.

\n' + + '\n' + + '

Чтобы перейти из текущего раздела интерфейса в предыдущий, нажмите Shift+Tab.

\n' + + '\n' + + '

Вкладки разделов интерфейса расположены в следующем порядке:

\n' + + '\n' + + '
    \n' + + '
  1. Панель меню
  2. \n' + + '
  3. Группы панели инструментов
  4. \n' + + '
  5. Боковая панель
  6. \n' + + '
  7. Путь элементов нижнего колонтитула
  8. \n' + + '
  9. Подсчет слов/символов в нижнем колонтитуле
  10. \n' + + '
  11. Брендовая ссылка в нижнем колонтитуле
  12. \n' + + '
  13. Угол для изменения размера окна редактора
  14. \n' + + '
\n' + + '\n' + + '

Если раздел интерфейса отсутствует, он пропускается.

\n' + + '\n' + + '

Если при управлении с клавиатуры фокус находится на нижнем колонтитуле, а видимая боковая панель отсутствует, то при нажатии сочетания клавиш Shift+Tab\n' + + ' фокус переносится на первую группу панели инструментов, а не на последнюю.

\n' + + '\n' + + '

Переход между элементами внутри разделов пользовательского интерфейса

\n' + + '\n' + + '

Чтобы перейти от текущего элемента интерфейса к следующему, нажмите соответствующую клавишу со стрелкой.

\n' + + '\n' + + '

Клавиши со стрелками влево и вправо позволяют

\n' + + '\n' + + '
    \n' + + '
  • перемещаться между разными меню в панели меню.
  • \n' + + '
  • открывать разделы меню.
  • \n' + + '
  • перемещаться между кнопками в группе панели инструментов.
  • \n' + + '
  • перемещаться между элементами в пути элементов нижнего колонтитула.
  • \n' + + '
\n' + + '\n' + + '

Клавиши со стрелками вниз и вверх позволяют

\n' + + '\n' + + '
    \n' + + '
  • перемещаться между элементами одного меню.
  • \n' + + '
  • перемещаться между элементами всплывающего меню в панели инструментов.
  • \n' + + '
\n' + + '\n' + + '

При использовании клавиш со стрелками вы будете циклически перемещаться по элементам в пределах выбранного раздела интерфейса.

\n' + + '\n' + + '

Чтобы закрыть открытое меню, его раздел или всплывающее меню, нажмите клавишу Esc.

\n' + + '\n' + + '

Если фокус находится наверху какого-либо раздела интерфейса, нажатие клавиши Esc также приведет\n' + + ' к выходу из режима управления с помощью клавиатуры.

\n' + + '\n' + + '

Использование элемента меню или кнопки на панели инструментов

\n' + + '\n' + + '

Когда элемент меню или кнопка панели инструментов будут выделены, нажмите Return, Enter\n' + + ' или Space, чтобы их активировать.

\n' + + '\n' + + '

Управление в диалоговом окне без вкладок

\n' + + '\n' + + '

При открытии диалогового окна без вкладок фокус переносится на первый интерактивный компонент.

\n' + + '\n' + + '

Для перехода между интерактивными компонентами диалогового окна нажимайте Tab или Shift+Tab.

\n' + + '\n' + + '

Управление в диалоговом окне с вкладками

\n' + + '\n' + + '

При открытии диалогового окна с вкладками фокус переносится на первую кнопку в меню вкладок.

\n' + + '\n' + + '

Для перехода между интерактивными компонентами этой вкладки диалогового окна нажимайте Tab или\n' + + ' Shift+Tab.

\n' + + '\n' + + '

Для перехода на другую вкладку диалогового окна переместите фокус на меню вкладок, а затем используйте клавиши со стрелками\n' + + ' для циклического переключения между доступными вкладками.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/sk.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/sk.js new file mode 100644 index 0000000..60cc628 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/sk.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.sk', +'

Začíname s navigáciou pomocou klávesnice

\n' + + '\n' + + '
\n' + + '
Prejsť na panel s ponukami
\n' + + '
Windows alebo Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Prejsť na panel nástrojov
\n' + + '
Windows alebo Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Prejsť na pätičku
\n' + + '
Windows alebo Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Zaostriť na oznámenie
\n' + + '
Windows alebo Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Prejsť na kontextový panel nástrojov
\n' + + '
Windows, Linux alebo macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

Navigácia začne pri prvej položke používateľského rozhrania, ktorá bude zvýraznená alebo v prípade prvej položky\n' + + ' cesty k pätičke podčiarknutá.

\n' + + '\n' + + '

Navigácia medzi časťami používateľského rozhrania

\n' + + '\n' + + '

Ak sa chcete posunúť z jednej časti používateľského rozhrania do druhej, stlačte tlačidlo Tab.

\n' + + '\n' + + '

Ak sa chcete posunúť z jednej časti používateľského rozhrania do predchádzajúcej, stlačte tlačidlá Shift + Tab.

\n' + + '\n' + + '

Poradie prepínania medzi týmito časťami používateľského rozhrania pri stláčaní tlačidla Tab:

\n' + + '\n' + + '
    \n' + + '
  1. Panel s ponukou
  2. \n' + + '
  3. Každá skupina panela nástrojov
  4. \n' + + '
  5. Bočný panel
  6. \n' + + '
  7. Cesta k prvku v pätičke
  8. \n' + + '
  9. Prepínač počtu slov v pätičke
  10. \n' + + '
  11. Odkaz na informácie o značke v pätičke
  12. \n' + + '
  13. Úchyt na zmenu veľkosti editora v pätičke
  14. \n' + + '
\n' + + '\n' + + '

Ak nejaká časť používateľského rozhrania nie je prítomná, preskočí sa.

\n' + + '\n' + + '

Ak je pätička vybratá na navigáciu pomocou klávesnice a nie je viditeľný bočný panel, stlačením klávesov Shift+Tab\n' + + ' prejdete na prvú skupinu panela nástrojov, nie na poslednú.

\n' + + '\n' + + '

Navigácia v rámci častí používateľského rozhrania

\n' + + '\n' + + '

Ak sa chcete posunúť z jedného prvku používateľského rozhrania na ďalší, stlačte príslušný kláves so šípkou.

\n' + + '\n' + + '

Klávesy so šípkami doľava a doprava

\n' + + '\n' + + '
    \n' + + '
  • umožňujú presun medzi ponukami na paneli ponúk,
  • \n' + + '
  • otvárajú podponuku v rámci ponuky,
  • \n' + + '
  • umožňujú presun medzi tlačidlami v skupine panelov nástrojov,
  • \n' + + '
  • umožňujú presun medzi položkami cesty prvku v pätičke.
  • \n' + + '
\n' + + '\n' + + '

Klávesy so šípkami dole a hore

\n' + + '\n' + + '
    \n' + + '
  • umožňujú presun medzi položkami ponuky,
  • \n' + + '
  • umožňujú presun medzi položkami v kontextovej ponuke panela nástrojov.
  • \n' + + '
\n' + + '\n' + + '

Klávesy so šípkami vykonávajú prepínanie v rámci vybranej časti používateľského rozhrania.

\n' + + '\n' + + '

Ak chcete zatvoriť otvorenú ponuku, otvorenú podponuku alebo otvorenú kontextovú ponuku, stlačte kláves Esc.

\n' + + '\n' + + '

Ak je aktuálne vybratá horná časť konkrétneho používateľského rozhrania, stlačením klávesu Esc úplne ukončíte tiež\n' + + ' navigáciu pomocou klávesnice.

\n' + + '\n' + + '

Vykonanie príkazu položky ponuky alebo tlačidla panela nástrojov

\n' + + '\n' + + '

Keď je zvýraznená požadovaná položka ponuky alebo tlačidlo panela nástrojov, stlačením klávesov Return, Enter\n' + + ' alebo medzerníka vykonáte príslušný príkaz položky.

\n' + + '\n' + + '

Navigácia v dialógových oknách bez záložiek

\n' + + '\n' + + '

Pri otvorení dialógových okien bez záložiek prejdete na prvý interaktívny komponent.

\n' + + '\n' + + '

Medzi interaktívnymi dialógovými komponentmi môžete prechádzať stlačením klávesov Tab alebo Shift+Tab.

\n' + + '\n' + + '

Navigácia v dialógových oknách so záložkami

\n' + + '\n' + + '

Pri otvorení dialógových okien so záložkami prejdete na prvé tlačidlo v ponuke záložiek.

\n' + + '\n' + + '

Medzi interaktívnymi komponentmi tejto dialógovej záložky môžete prechádzať stlačením klávesov Tab alebo\n' + + ' Shift+Tab.

\n' + + '\n' + + '

Ak chcete prepnúť na ďalšiu záložku dialógového okna, prejdite do ponuky záložiek a potom môžete stlačením príslušného klávesu so šípkou\n' + + ' prepínať medzi dostupnými záložkami.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/sl_SI.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/sl_SI.js new file mode 100644 index 0000000..2b25f5a --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/sl_SI.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.sl_SI', +'

Začetek krmarjenja s tipkovnico

\n' + + '\n' + + '
\n' + + '
Fokus na menijsko vrstico
\n' + + '
Windows ali Linux: Alt + F9
\n' + + '
macOS: ⌥F9
\n' + + '
Fokus na orodno vrstico
\n' + + '
Windows ali Linux: Alt + F10
\n' + + '
macOS: ⌥F10
\n' + + '
Fokus na nogo
\n' + + '
Windows ali Linux: Alt + F11
\n' + + '
macOS: ⌥F11
\n' + + '
Označitev obvestila
\n' + + '
Windows ali Linux: Alt + F12
\n' + + '
macOS: ⌥F12
\n' + + '
Fokus na kontekstualno orodno vrstico
\n' + + '
Windows, Linux ali macOS: Ctrl + F9
\n' + + '
\n' + + '\n' + + '

Krmarjenje se bo začelo s prvim elementom uporabniškega vmesnika, ki bo izpostavljena ali podčrtan, če gre za prvi element na\n' + + ' poti do elementa noge.

\n' + + '\n' + + '

Krmarjenje med razdelki uporabniškega vmesnika

\n' + + '\n' + + '

Če se želite pomakniti z enega dela uporabniškega vmesnika na naslednjega, pritisnite tabulatorko.

\n' + + '\n' + + '

Če se želite pomakniti z enega dela uporabniškega vmesnika na prejšnjega, pritisnite shift + tabulatorko.

\n' + + '\n' + + '

Zaporedje teh razdelkov uporabniškega vmesnika, ko pritiskate tabulatorko, je:

\n' + + '\n' + + '
    \n' + + '
  1. Menijska vrstica
  2. \n' + + '
  3. Posamezne skupine orodne vrstice
  4. \n' + + '
  5. Stranska vrstica
  6. \n' + + '
  7. Pod do elementa v nogi
  8. \n' + + '
  9. Gumb za preklop štetja besed v nogi
  10. \n' + + '
  11. Povezava do blagovne znamke v nogi
  12. \n' + + '
  13. Ročaj za spreminjanje velikosti urejevalnika v nogi
  14. \n' + + '
\n' + + '\n' + + '

Če razdelek uporabniškega vmesnika ni prisoten, je preskočen.

\n' + + '\n' + + '

Če ima noga fokus za krmarjenje s tipkovnico in ni vidne stranske vrstice, s pritiskom na shift + tabulatorko\n' + + ' fokus premaknete na prvo skupino orodne vrstice, ne zadnjo.

\n' + + '\n' + + '

Krmarjenje v razdelkih uporabniškega vmesnika

\n' + + '\n' + + '

Če se želite premakniti z enega elementa uporabniškega vmesnika na naslednjega, pritisnite ustrezno puščično tipko.

\n' + + '\n' + + '

Leva in desna puščična tipka

\n' + + '\n' + + '
    \n' + + '
  • omogočata premikanje med meniji v menijski vrstici.
  • \n' + + '
  • odpreta podmeni v meniju.
  • \n' + + '
  • omogočata premikanje med gumbi v skupini orodne vrstice.
  • \n' + + '
  • omogočata premikanje med elementi na poti do elementov noge.
  • \n' + + '
\n' + + '\n' + + '

Spodnja in zgornja puščična tipka

\n' + + '\n' + + '
    \n' + + '
  • omogočata premikanje med elementi menija.
  • \n' + + '
  • omogočata premikanje med elementi v pojavnem meniju orodne vrstice.
  • \n' + + '
\n' + + '\n' + + '

Puščične tipke omogočajo kroženje znotraj razdelka uporabniškega vmesnika, na katerem je fokus.

\n' + + '\n' + + '

Če želite zapreti odprt meni, podmeni ali pojavni meni, pritisnite tipko Esc.

\n' + + '\n' + + '

Če je trenutni fokus na »vrhu« določenega razdelka uporabniškega vmesnika, s pritiskom tipke Esc zaprete\n' + + ' tudi celotno krmarjenje s tipkovnico.

\n' + + '\n' + + '

Izvajanje menijskega elementa ali gumba orodne vrstice

\n' + + '\n' + + '

Ko je označen želeni menijski element ali orodja vrstica, pritisnite vračalko, Enter\n' + + ' ali preslednico, da izvedete element.

\n' + + '\n' + + '

Krmarjenje po pogovornih oknih brez zavihkov

\n' + + '\n' + + '

Ko odprete pogovorno okno brez zavihkov, ima fokus prva interaktivna komponenta.

\n' + + '\n' + + '

Med interaktivnimi komponentami pogovornega okna se premikate s pritiskom tabulatorke ali kombinacije tipke shift + tabulatorke.

\n' + + '\n' + + '

Krmarjenje po pogovornih oknih z zavihki

\n' + + '\n' + + '

Ko odprete pogovorno okno z zavihki, ima fokus prvi gumb v meniju zavihka.

\n' + + '\n' + + '

Med interaktivnimi komponentami tega zavihka pogovornega okna se premikate s pritiskom tabulatorke ali\n' + + ' kombinacije tipke shift + tabulatorke.

\n' + + '\n' + + '

Na drug zavihek pogovornega okna preklopite tako, da fokus prestavite na meni zavihka in nato pritisnete ustrezno puščično\n' + + ' tipko, da se pomaknete med razpoložljivimi zavihki.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/sv_SE.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/sv_SE.js new file mode 100644 index 0000000..c30f2f2 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/sv_SE.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.sv_SE', +'

Påbörja tangentbordsnavigering

\n' + + '\n' + + '
\n' + + '
Fokusera på menyraden
\n' + + '
Windows eller Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Fokusera på verktygsraden
\n' + + '
Windows eller Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Fokusera på verktygsraden
\n' + + '
Windows eller Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Fokusera aviseringen
\n' + + '
Windows eller Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Fokusera på en snabbverktygsrad
\n' + + '
Windows, Linux eller macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

Navigeringen börjar vid det första gränssnittsobjektet, vilket är markerat eller understruket om det gäller det första objektet i\n' + + ' sidfotens elementsökväg.

\n' + + '\n' + + '

Navigera mellan UI-avsnitt

\n' + + '\n' + + '

Flytta från ett UI-avsnitt till nästa genom att trycka på Tabb.

\n' + + '\n' + + '

Flytta från ett UI-avsnitt till det föregående genom att trycka på Skift+Tabb.

\n' + + '\n' + + '

Tabb-ordningen för dessa UI-avsnitt är:

\n' + + '\n' + + '
    \n' + + '
  1. Menyrad
  2. \n' + + '
  3. Varje verktygsradsgrupp
  4. \n' + + '
  5. Sidoruta
  6. \n' + + '
  7. Elementsökväg i sidfoten
  8. \n' + + '
  9. Växlingsknapp för ordantal i sidfoten
  10. \n' + + '
  11. Varumärkeslänk i sidfoten
  12. \n' + + '
  13. Storlekshandtag för redigeraren i sidfoten
  14. \n' + + '
\n' + + '\n' + + '

Om ett UI-avsnitt inte finns hoppas det över.

\n' + + '\n' + + '

Om sidfoten har fokus på tangentbordsnavigering, och det inte finns någon synlig sidoruta, flyttas fokus till den första verktygsradsgruppen\n' + + ' när du trycker på Skift+Tabb, inte till den sista.

\n' + + '\n' + + '

Navigera i UI-avsnitt

\n' + + '\n' + + '

Flytta från ett UI-element till nästa genom att trycka på motsvarande piltangent.

\n' + + '\n' + + '

Vänsterpil och högerpil

\n' + + '\n' + + '
    \n' + + '
  • flytta mellan menyer på menyraden.
  • \n' + + '
  • öppna en undermeny på en meny.
  • \n' + + '
  • flytta mellan knappar i en verktygsradgrupp.
  • \n' + + '
  • flytta mellan objekt i sidfotens elementsökväg.
  • \n' + + '
\n' + + '\n' + + '

Nedpil och uppil

\n' + + '\n' + + '
    \n' + + '
  • flytta mellan menyalternativ på en meny.
  • \n' + + '
  • flytta mellan alternativ på en popup-meny på verktygsraden.
  • \n' + + '
\n' + + '\n' + + '

Piltangenterna cirkulerar inom det fokuserade UI-avsnittet.

\n' + + '\n' + + '

Tryck på Esc-tangenten om du vill stänga en öppen meny, undermeny eller popup-meny.

\n' + + '\n' + + '

Om det aktuella fokuset är högst upp i ett UI-avsnitt avlutas även tangentbordsnavigeringen helt när\n' + + ' du trycker på Esc-tangenten.

\n' + + '\n' + + '

Köra ett menyalternativ eller en verktygfältsknapp

\n' + + '\n' + + '

När menyalternativet eller verktygsradsknappen är markerad trycker du på Retur, Enter\n' + + ' eller blanksteg för att köra alternativet.

\n' + + '\n' + + '

Navigera i dialogrutor utan flikar

\n' + + '\n' + + '

I dialogrutor utan flikar är den första interaktiva komponenten i fokus när dialogrutan öppnas.

\n' + + '\n' + + '

Navigera mellan interaktiva dialogkomponenter genom att trycka på Tabb eller Skift+Tabb.

\n' + + '\n' + + '

Navigera i dialogrutor med flikar

\n' + + '\n' + + '

I dialogrutor utan flikar är den första knappen på flikmenyn i fokus när dialogrutan öppnas.

\n' + + '\n' + + '

Navigera mellan interaktiva komponenter på dialogrutefliken genom att trycka på Tabb eller\n' + + ' Skift+Tabb.

\n' + + '\n' + + '

Växla till en annan dialogruta genom att fokusera på flikmenyn och sedan trycka på motsvarande piltangent\n' + + ' för att cirkulera mellan de tillgängliga flikarna.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/th_TH.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/th_TH.js new file mode 100644 index 0000000..562fe7a --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/th_TH.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.th_TH', +'

เริ่มต้นการนำทางด้วยแป้นพิมพ์

\n' + + '\n' + + '
\n' + + '
โฟกัสที่แถบเมนู
\n' + + '
Windows หรือ Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
โฟกัสที่แถบเครื่องมือ
\n' + + '
Windows หรือ Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
โฟกัสที่ส่วนท้าย
\n' + + '
Windows หรือ Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
โฟกัสไปที่การแจ้งเตือน
\n' + + '
Windows หรือ Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
โฟกัสที่แถบเครื่องมือตามบริบท
\n' + + '
Windows, Linux หรือ macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

การนำทางจะเริ่มที่รายการ UI แรก ซึ่งจะมีการไฮไลต์หรือขีดเส้นใต้ไว้ในกรณีที่รายการแรกอยู่ใน\n' + + ' พาธองค์ประกอบส่วนท้าย

\n' + + '\n' + + '

การนำทางระหว่างส่วนต่างๆ ของ UI

\n' + + '\n' + + '

ในการย้ายจากส่วน UI หนึ่งไปยังส่วนถัดไป ให้กด Tab

\n' + + '\n' + + '

ในการย้ายจากส่วน UI หนึ่งไปยังส่วนก่อนหน้า ให้กด Shift+Tab

\n' + + '\n' + + '

ลำดับแท็บของส่วนต่างๆ ของ UI คือ:

\n' + + '\n' + + '
    \n' + + '
  1. แถบเมนู
  2. \n' + + '
  3. แต่ละกลุ่มแถบเครื่องมือ
  4. \n' + + '
  5. แถบข้าง
  6. \n' + + '
  7. พาธองค์ประกอบในส่วนท้าย
  8. \n' + + '
  9. ปุ่มสลับเปิด/ปิดจำนวนคำในส่วนท้าย
  10. \n' + + '
  11. ลิงก์ชื่อแบรนด์ในส่วนท้าย
  12. \n' + + '
  13. จุดจับปรับขนาดของตัวแก้ไขในส่วนท้าย
  14. \n' + + '
\n' + + '\n' + + '

หากส่วน UI ไม่ปรากฏ แสดงว่าถูกข้ามไป

\n' + + '\n' + + '

หากส่วนท้ายมีการโฟกัสการนำทางแป้นพิมพ์และไม่มีแถบข้างปรากฏ การกด Shift+Tab\n' + + ' จะย้ายการโฟกัสไปที่กลุ่มแถบเครื่องมือแรก ไม่ใช่สุดท้าย

\n' + + '\n' + + '

การนำทางภายในส่วนต่างๆ ของ UI

\n' + + '\n' + + '

ในการย้ายจากองค์ประกอบ UI หนึ่งไปยังองค์ประกอบส่วนถัดไป ให้กดปุ่มลูกศรที่เหมาะสม

\n' + + '\n' + + '

ปุ่มลูกศรซ้ายและขวา

\n' + + '\n' + + '
    \n' + + '
  • ย้ายไปมาระหว่างเมนูต่างๆ ในแถบเมนู
  • \n' + + '
  • เปิดเมนูย่อยในเมนู
  • \n' + + '
  • ย้ายไปมาระหว่างปุ่มต่างๆ ในกลุ่มแถบเครื่องมือ
  • \n' + + '
  • ย้ายไปมาระหว่างรายการต่างๆ ในพาธองค์ประกอบของส่วนท้าย
  • \n' + + '
\n' + + '\n' + + '

ปุ่มลูกศรลงและขึ้น

\n' + + '\n' + + '
    \n' + + '
  • ย้ายไปมาระหว่างรายการเมนูต่างๆ ในเมนู
  • \n' + + '
  • ย้ายไปมาระหว่างรายการต่างๆ ในเมนูป๊อบอัพแถบเครื่องมือ
  • \n' + + '
\n' + + '\n' + + '

ปุ่มลูกศรจะเลื่อนไปมาภายในส่วน UI ที่โฟกัส

\n' + + '\n' + + '

ในการปิดเมนูที่เปิดอยู่ เมนูย่อยที่เปิดอยู่ หรือเมนูป๊อบอัพที่เปิดอยู่ ให้กดปุ่ม Esc

\n' + + '\n' + + '

หากโฟกัสปัจจุบันอยู่ที่ ‘ด้านบนสุด’ ของส่วน UI เฉพาะ การกดปุ่ม Esc จะทำให้ออกจาก\n' + + ' การนำทางด้วยแป้นพิมพ์ทั้งหมดเช่นกัน

\n' + + '\n' + + '

การดำเนินการรายการเมนูหรือปุ่มในแถบเครื่องมือ

\n' + + '\n' + + '

เมื่อไฮไลต์รายการเมนูหรือปุ่มในแถบเครื่องมือที่ต้องการ ให้กด Return, Enter\n' + + ' หรือ Space bar เพื่อดำเนินการรายการดังกล่าว

\n' + + '\n' + + '

การนำทางสำหรับกล่องโต้ตอบที่ไม่อยู่ในแท็บ

\n' + + '\n' + + '

ในกล่องโต้ตอบที่ไม่อยู่ในแท็บ จะโฟกัสที่ส่วนประกอบเชิงโต้ตอบแรกเมื่อกล่องโต้ตอบเปิด

\n' + + '\n' + + '

นำทางระหว่างส่วนประกอบเชิงโต้ตอบต่างๆ ของกล่องโต้ตอบ โดยการกด Tab หรือ Shift+Tab

\n' + + '\n' + + '

การนำทางสำหรับกล่องโต้ตอบที่อยู่ในแท็บ

\n' + + '\n' + + '

ในกล่องโต้ตอบที่อยู่ในแท็บ จะโฟกัสที่ปุ่มแรกในเมนูแท็บเมื่อกล่องโต้ตอบเปิด

\n' + + '\n' + + '

นำทางระหว่างส่วนประกอบเชิงโต้ตอบต่างๆ ของแท็บกล่องโต้ตอบนี้โดยการกด Tab หรือ\n' + + ' Shift+Tab

\n' + + '\n' + + '

สลับไปยังแท็บกล่องโต้ตอบอื่นโดยการเลือกโฟกัสที่เมนูแท็บ แล้วกดปุ่มลูกศรที่เหมาะสม\n' + + ' เพื่อเลือกแท็บที่ใช้ได้

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/tr.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/tr.js new file mode 100644 index 0000000..37f39b0 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/tr.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.tr', +'

Klavyeyle gezintiyi başlatma

\n' + + '\n' + + '
\n' + + '
Menü çubuğuna odaklan
\n' + + '
Windows veya Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Araç çubuğuna odaklan
\n' + + '
Windows veya Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Alt bilgiye odaklan
\n' + + '
Windows veya Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Bildirime odakla
\n' + + '
Windows veya Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Bağlamsal araç çubuğuna odaklan
\n' + + '
Windows, Linux veya macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

Gezinti ilk kullanıcı arabirimi öğesinden başlar, bu öğe vurgulanır ya da ilk öğe, Alt bilgi elemanı\n' + + ' yolundaysa altı çizilir.

\n' + + '\n' + + '

Kullanıcı arabirimi bölümleri arasında gezinme

\n' + + '\n' + + '

Sonraki kullanıcı arabirimi bölümüne gitmek için Sekme tuşuna basın.

\n' + + '\n' + + '

Önceki kullanıcı arabirimi bölümüne gitmek için Shift+Sekme tuşlarına basın.

\n' + + '\n' + + '

Bu kullanıcı arabirimi bölümlerinin Sekme sırası:

\n' + + '\n' + + '
    \n' + + '
  1. Menü çubuğu
  2. \n' + + '
  3. Her araç çubuğu grubu
  4. \n' + + '
  5. Kenar çubuğu
  6. \n' + + '
  7. Alt bilgide öğe yolu
  8. \n' + + '
  9. Alt bilgide sözcük sayısı geçiş düğmesi
  10. \n' + + '
  11. Alt bilgide marka bağlantısı
  12. \n' + + '
  13. Alt bilgide düzenleyiciyi yeniden boyutlandırma tutamacı
  14. \n' + + '
\n' + + '\n' + + '

Kullanıcı arabirimi bölümü yoksa atlanır.

\n' + + '\n' + + '

Alt bilgide klavyeyle gezinti odağı yoksa ve görünür bir kenar çubuğu mevcut değilse Shift+Sekme tuşlarına basıldığında\n' + + ' odak son araç çubuğu yerine ilk araç çubuğu grubuna taşınır.

\n' + + '\n' + + '

Kullanıcı arabirimi bölümleri içinde gezinme

\n' + + '\n' + + '

Sonraki kullanıcı arabirimi elemanına gitmek için uygun Ok tuşuna basın.

\n' + + '\n' + + '

Sol ve Sağ ok tuşları

\n' + + '\n' + + '
    \n' + + '
  • menü çubuğundaki menüler arasında hareket eder.
  • \n' + + '
  • menüde bir alt menü açar.
  • \n' + + '
  • araç çubuğu grubundaki düğmeler arasında hareket eder.
  • \n' + + '
  • alt bilginin öğe yolundaki öğeler arasında hareket eder.
  • \n' + + '
\n' + + '\n' + + '

Aşağı ve Yukarı ok tuşları

\n' + + '\n' + + '
    \n' + + '
  • menüdeki menü öğeleri arasında hareket eder.
  • \n' + + '
  • araç çubuğu açılır menüsündeki öğeler arasında hareket eder.
  • \n' + + '
\n' + + '\n' + + '

Ok tuşları, odaklanılan kullanıcı arabirimi bölümü içinde döngüsel olarak hareket eder.

\n' + + '\n' + + '

Açık bir menüyü, açık bir alt menüyü veya açık bir açılır menüyü kapatmak için Esc tuşuna basın.

\n' + + '\n' + + '

Geçerli odak belirli bir kullanıcı arabirimi bölümünün "üst" kısmındaysa Esc tuşuna basıldığında\n' + + ' klavyeyle gezintiden de tamamen çıkılır.

\n' + + '\n' + + '

Menü öğesini veya araç çubuğu düğmesini yürütme

\n' + + '\n' + + '

İstediğiniz menü öğesi veya araç çubuğu düğmesi vurgulandığında Return, Enter\n' + + ' veya Ara çubuğu tuşuna basın.

\n' + + '\n' + + '

Sekme bulunmayan iletişim kutularında gezinme

\n' + + '\n' + + '

Sekme bulunmayan iletişim kutularında, iletişim kutusu açıldığında ilk etkileşimli bileşene odaklanılır.

\n' + + '\n' + + '

Etkileşimli iletişim kutusu bileşenleri arasında gezinmek için Sekme veya Shift+ Sekme tuşlarına basın.

\n' + + '\n' + + '

Sekmeli iletişim kutularında gezinme

\n' + + '\n' + + '

Sekmeli iletişim kutularında, iletişim kutusu açıldığında sekme menüsündeki ilk düğmeye odaklanılır.

\n' + + '\n' + + '

Bu iletişim kutusu sekmesinin etkileşimli bileşenleri arasında gezinmek için Sekme veya\n' + + ' Shift+Sekme tuşlarına basın.

\n' + + '\n' + + '

Mevcut sekmeler arasında geçiş yapmak için sekme menüsüne odaklanıp uygun Ok tuşuna basarak\n' + + ' başka bir iletişim kutusu sekmesine geçiş yapın.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/uk.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/uk.js new file mode 100644 index 0000000..028d4a4 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/uk.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.uk', +'

Початок роботи з навігацією за допомогою клавіатури

\n' + + '\n' + + '
\n' + + '
Фокус на рядок меню
\n' + + '
Windows або Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Фокус на панелі інструментів
\n' + + '
Windows або Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Фокус на розділі "Нижній колонтитул"
\n' + + '
Windows або Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Фокус на сповіщення
\n' + + '
Windows або Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Фокус на контекстній панелі інструментів
\n' + + '
Windows, Linux або macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

Навігація почнеться з першого елемента інтерфейсу користувача, який буде виділено або підкреслено в разі, якщо перший елемент знаходиться в\n' + + ' шляху до елемента "Нижній колонтитул".

\n' + + '\n' + + '

Навігація між розділами інтерфейсу користувача

\n' + + '\n' + + '

Щоб перейти з одного розділу інтерфейсу користувача до наступного розділу, натисніть клавішу Tab.

\n' + + '\n' + + '

Щоб перейти з одного розділу інтерфейсу користувача до попереднього розділу, натисніть сполучення клавіш Shift+Tab.

\n' + + '\n' + + '

Порядок Вкладок цих розділів інтерфейсу користувача такий:

\n' + + '\n' + + '
    \n' + + '
  1. Рядок меню
  2. \n' + + '
  3. Кожна група панелей інструментів
  4. \n' + + '
  5. Бічна панель
  6. \n' + + '
  7. Шлях до елементів у розділі "Нижній колонтитул"
  8. \n' + + '
  9. Кнопка перемикача "Кількість слів" у розділі "Нижній колонтитул"
  10. \n' + + '
  11. Посилання на брендинг у розділі "Нижній колонтитул"
  12. \n' + + '
  13. Маркер змінення розміру в розділі "Нижній колонтитул"
  14. \n' + + '
\n' + + '\n' + + '

Якщо розділ інтерфейсу користувача відсутній, він пропускається.

\n' + + '\n' + + '

Якщо фокус навігації клавіатури знаходиться на розділі "Нижній колонтитул", але користувач не бачить видиму бічну панель, натисніть Shift+Tab,\n' + + ' щоб перемістити фокус на першу групу панелі інструментів, а не на останню.

\n' + + '\n' + + '

Навігація в межах розділів інтерфейсу користувача

\n' + + '\n' + + '

Щоб перейти з одного елементу інтерфейсу користувача до наступного, натисніть відповідну клавішу зі стрілкою.

\n' + + '\n' + + '

Клавіші зі стрілками Ліворуч і Праворуч

\n' + + '\n' + + '
    \n' + + '
  • переміщують між меню в рядку меню.
  • \n' + + '
  • відкривають вкладене меню в меню.
  • \n' + + '
  • переміщують користувача між кнопками в групі панелі інструментів.
  • \n' + + '
  • переміщують між елементами в шляху до елементів у розділі "Нижній колонтитул".
  • \n' + + '
\n' + + '\n' + + '

Клавіші зі стрілками Вниз і Вгору

\n' + + '\n' + + '
    \n' + + '
  • переміщують між елементами меню в меню.
  • \n' + + '
  • переміщують між елементами в спливаючому меню панелі інструментів.
  • \n' + + '
\n' + + '\n' + + '

Клавіші зі стрілками переміщують фокус циклічно в межах розділу інтерфейсу користувача, на якому знаходиться фокус.

\n' + + '\n' + + '

Щоб закрити відкрите меню, відкрите вкладене меню або відкрите спливаюче меню, натисніть клавішу Esc.

\n' + + '\n' + + '

Якщо поточний фокус знаходиться на верхньому рівні певного розділу інтерфейсу користувача, натискання клавіші Esc також виконує вихід\n' + + ' з навігації за допомогою клавіатури повністю.

\n' + + '\n' + + '

Виконання елементу меню або кнопки панелі інструментів

\n' + + '\n' + + '

Коли потрібний елемент меню або кнопку панелі інструментів виділено, натисніть клавіші Return, Enter,\n' + + ' або Пробіл, щоб виконати цей елемент.

\n' + + '\n' + + '

Навігація по діалоговим вікнам без вкладок

\n' + + '\n' + + '

У діалогових вікнах без вкладок перший інтерактивний компонент приймає фокус, коли відкривається діалогове вікно.

\n' + + '\n' + + '

Переходьте між інтерактивними компонентами діалогового вікна, натискаючи клавіші Tab або Shift+Tab.

\n' + + '\n' + + '

Навігація по діалоговим вікнам з вкладками

\n' + + '\n' + + '

У діалогових вікнах із вкладками перша кнопка в меню вкладки приймає фокус, коли відкривається діалогове вікно.

\n' + + '\n' + + '

Переходьте між інтерактивними компонентами цієї вкладки діалогового вікна, натискаючи клавіші Tab або\n' + + ' Shift+Tab.

\n' + + '\n' + + '

Щоб перейти на іншу вкладку діалогового вікна, перемістіть фокус на меню вкладки, а потім натисніть відповідну клавішу зі стрілкою,\n' + + ' щоб циклічно переходити по доступним вкладкам.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/vi.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/vi.js new file mode 100644 index 0000000..d8eda11 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/vi.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.vi', +'

Bắt đầu điều hướng bàn phím

\n' + + '\n' + + '
\n' + + '
Tập trung vào thanh menu
\n' + + '
Windows hoặc Linux: Alt+F9
\n' + + '
macOS: ⌥F9
\n' + + '
Tập trung vào thanh công cụ
\n' + + '
Windows hoặc Linux: Alt+F10
\n' + + '
macOS: ⌥F10
\n' + + '
Tập trung vào chân trang
\n' + + '
Windows hoặc Linux: Alt+F11
\n' + + '
macOS: ⌥F11
\n' + + '
Tập trung vào thông báo
\n' + + '
Windows hoặc Linux: Alt+F12
\n' + + '
macOS: ⌥F12
\n' + + '
Tập trung vào thanh công cụ ngữ cảnh
\n' + + '
Windows, Linux hoặc macOS: Ctrl+F9
\n' + + '
\n' + + '\n' + + '

Điều hướng sẽ bắt đầu từ mục UI đầu tiên. Mục này sẽ được tô sáng hoặc có gạch dưới (nếu là mục đầu tiên trong\n' + + ' đường dẫn phần tử Chân trang).

\n' + + '\n' + + '

Di chuyển qua lại giữa các phần UI

\n' + + '\n' + + '

Để di chuyển từ một phần UI sang phần tiếp theo, ấn Tab.

\n' + + '\n' + + '

Để di chuyển từ một phần UI về phần trước đó, ấn Shift+Tab.

\n' + + '\n' + + '

Thứ tự Tab của các phần UI này như sau:

\n' + + '\n' + + '
    \n' + + '
  1. Thanh menu
  2. \n' + + '
  3. Từng nhóm thanh công cụ
  4. \n' + + '
  5. Thanh bên
  6. \n' + + '
  7. Đường dẫn phần tử trong chân trang
  8. \n' + + '
  9. Nút chuyển đổi đếm chữ ở chân trang
  10. \n' + + '
  11. Liên kết thương hiệu ở chân trang
  12. \n' + + '
  13. Núm điều tác chỉnh kích cỡ trình soạn thảo ở chân trang
  14. \n' + + '
\n' + + '\n' + + '

Nếu người dùng không thấy một phần UI, thì có nghĩa phần đó bị bỏ qua.

\n' + + '\n' + + '

Nếu ở chân trang có tính năng tập trung điều hướng bàn phím, mà không có thanh bên nào hiện hữu, thao tác ấn Shift+Tab\n' + + ' sẽ chuyển hướng tập trung vào nhóm thanh công cụ đầu tiên, không phải cuối cùng.

\n' + + '\n' + + '

Di chuyển qua lại trong các phần UI

\n' + + '\n' + + '

Để di chuyển từ một phần tử UI sang phần tiếp theo, ấn phím Mũi tên tương ứng cho phù hợp.

\n' + + '\n' + + '

Các phím mũi tên TráiPhải

\n' + + '\n' + + '
    \n' + + '
  • di chuyển giữa các menu trong thanh menu.
  • \n' + + '
  • mở menu phụ trong một menu.
  • \n' + + '
  • di chuyển giữa các nút trong nhóm thanh công cụ.
  • \n' + + '
  • di chuyển giữa các mục trong đường dẫn phần tử của chân trang.
  • \n' + + '
\n' + + '\n' + + '

Các phím mũi tên Hướng xuốngHướng lên

\n' + + '\n' + + '
    \n' + + '
  • di chuyển giữa các mục menu trong menu.
  • \n' + + '
  • di chuyển giữa các mục trong menu thanh công cụ dạng bật lên.
  • \n' + + '
\n' + + '\n' + + '

Các phím mũi tên xoay vòng trong một phần UI tập trung.

\n' + + '\n' + + '

Để đóng một menu mở, một menu phụ đang mở, hoặc một menu dạng bật lên đang mở, hãy ấn phím Esc.

\n' + + '\n' + + '

Nếu trọng tâm hiện tại là ở phần “đầu” của một phần UI cụ thể, thao tác ấn phím Esc cũng sẽ thoát\n' + + ' toàn bộ phần điều hướng bàn phím.

\n' + + '\n' + + '

Thực hiện chức năng của một mục menu hoặc nút thanh công cụ

\n' + + '\n' + + '

Khi mục menu hoặc nút thanh công cụ muốn dùng được tô sáng, hãy ấn Return, Enter,\n' + + ' hoặc Phím cách để thực hiện chức năng mục đó.

\n' + + '\n' + + '

Điều hướng giữa các hộp thoại không có nhiều tab

\n' + + '\n' + + '

Trong các hộp thoại không có nhiều tab, khi hộp thoại mở ra, trọng tâm sẽ hướng vào thành phần tương tác đầu tiên.

\n' + + '\n' + + '

Di chuyển giữa các thành phần hộp thoại tương tác bằng cách ấn Tab hoặc Shift+Tab.

\n' + + '\n' + + '

Điều hướng giữa các hộp thoại có nhiều tab

\n' + + '\n' + + '

Trong các hộp thoại có nhiều tab, khi hộp thoại mở ra, trọng tâm sẽ hướng vào nút đầu tiên trong menu tab.

\n' + + '\n' + + '

Di chuyển giữa các thành phần tương tác của tab hộp thoại này bằng cách ấn Tab hoặc\n' + + ' Shift+Tab.

\n' + + '\n' + + '

Chuyển sang một tab hộp thoại khác bằng cách chuyển trọng tâm vào menu tab, rồi ấn phím Mũi tên phù hợp\n' + + ' để xoay vòng các tab hiện có.

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/zh_CN.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/zh_CN.js new file mode 100644 index 0000000..f7e73d1 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/zh_CN.js @@ -0,0 +1,87 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.zh_CN', +'

开始键盘导航

\n' + + '\n' + + '
\n' + + '
使菜单栏处于焦点
\n' + + '
Windows 或 Linux:Alt+F9
\n' + + '
macOS:⌥F9
\n' + + '
使工具栏处于焦点
\n' + + '
Windows 或 Linux:Alt+F10
\n' + + '
macOS:⌥F10
\n' + + '
使页脚处于焦点
\n' + + '
Windows 或 Linux:Alt+F11
\n' + + '
macOS:⌥F11
\n' + + '
使通知处于焦点
\n' + + '
Windows 或 Linux:Alt+F12
\n' + + '
macOS:⌥F12
\n' + + '
使上下文工具栏处于焦点
\n' + + '
Windows、Linux 或 macOS:Ctrl+F9
\n' + + '
\n' + + '\n' + + '

导航将在第一个 UI 项上开始,其中突出显示该项,或者对于页脚元素路径中的第一项,将为其添加下划线。

\n' + + '\n' + + '

在 UI 部分之间导航

\n' + + '\n' + + '

要从一个 UI 部分移至下一个,请按 Tab

\n' + + '\n' + + '

要从一个 UI 部分移至上一个,请按 Shift+Tab

\n' + + '\n' + + '

这些 UI 部分的 Tab 顺序为:

\n' + + '\n' + + '
    \n' + + '
  1. 菜单栏
  2. \n' + + '
  3. 每个工具栏组
  4. \n' + + '
  5. 边栏
  6. \n' + + '
  7. 页脚中的元素路径
  8. \n' + + '
  9. 页脚中的字数切换按钮
  10. \n' + + '
  11. 页脚中的品牌链接
  12. \n' + + '
  13. 页脚中的编辑器调整大小图柄
  14. \n' + + '
\n' + + '\n' + + '

如果不存在某个 UI 部分,则跳过它。

\n' + + '\n' + + '

如果键盘导航焦点在页脚,并且没有可见的边栏,则按 Shift+Tab 将焦点移至第一个工具栏组而非最后一个。

\n' + + '\n' + + '

在 UI 部分内导航

\n' + + '\n' + + '

要从一个 UI 元素移至下一个,请按相应的箭头键。

\n' + + '\n' + + '

箭头键

\n' + + '\n' + + '
    \n' + + '
  • 在菜单栏中的菜单之间移动。
  • \n' + + '
  • 打开菜单中的子菜单。
  • \n' + + '
  • 在工具栏组中的按钮之间移动。
  • \n' + + '
  • 在页脚的元素路径中的各项之间移动。
  • \n' + + '
\n' + + '\n' + + '

箭头键

\n' + + '\n' + + '
    \n' + + '
  • 在菜单中的菜单项之间移动。
  • \n' + + '
  • 在工具栏弹出菜单中的各项之间移动。
  • \n' + + '
\n' + + '\n' + + '

箭头键在具有焦点的 UI 部分内循环。

\n' + + '\n' + + '

要关闭打开的菜单、打开的子菜单或打开的弹出菜单,请按 Esc 键。

\n' + + '\n' + + '

如果当前的焦点在特定 UI 部分的“顶部”,则按 Esc 键还将完全退出键盘导航。

\n' + + '\n' + + '

执行菜单项或工具栏按钮

\n' + + '\n' + + '

当突出显示所需的菜单项或工具栏按钮时,按 ReturnEnter空格以执行该项。

\n' + + '\n' + + '

在非标签页式对话框中导航

\n' + + '\n' + + '

在非标签页式对话框中,当对话框打开时,第一个交互组件获得焦点。

\n' + + '\n' + + '

通过按 TabShift+Tab,在交互对话框组件之间导航。

\n' + + '\n' + + '

在标签页式对话框中导航

\n' + + '\n' + + '

在标签页式对话框中,当对话框打开时,标签页菜单中的第一个按钮获得焦点。

\n' + + '\n' + + '

通过按 TabShift+Tab,在此对话框的交互组件之间导航。

\n' + + '\n' + + '

通过将焦点移至另一对话框标签页的菜单,然后按相应的箭头键以在可用的标签页间循环,从而切换到该对话框标签页。

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/zh_TW.js b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/zh_TW.js new file mode 100644 index 0000000..5912770 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/js/i18n/keynav/zh_TW.js @@ -0,0 +1,93 @@ +tinymce.Resource.add('tinymce.html-i18n.help-keynav.zh_TW', +'

開始鍵盤瀏覽

\n' + + '\n' + + '
\n' + + '
跳至功能表列
\n' + + '
Windows 或 Linux:Alt+F9
\n' + + '
macOS:⌥F9
\n' + + '
跳至工具列
\n' + + '
Windows 或 Linux:Alt+F10
\n' + + '
macOS:⌥F10
\n' + + '
跳至頁尾
\n' + + '
Windows 或 Linux:Alt+F11
\n' + + '
macOS:⌥F11
\n' + + '
跳至通知
\n' + + '
Windows 或 Linux:Alt+F12
\n' + + '
macOS:⌥F12
\n' + + '
跳至關聯式工具列
\n' + + '
Windows、Linux 或 macOS:Ctrl+F9
\n' + + '
\n' + + '\n' + + '

瀏覽會從第一個 UI 項目開始,該項目會反白顯示,但如果是「頁尾」元素路徑的第一項,\n' + + ' 則加底線。

\n' + + '\n' + + '

在 UI 區段之間瀏覽

\n' + + '\n' + + '

從 UI 區段移至下一個,請按 Tab

\n' + + '\n' + + '

從 UI 區段移回上一個,請按 Shift+Tab

\n' + + '\n' + + '

這些 UI 區段的 Tab 順序如下:

\n' + + '\n' + + '
    \n' + + '
  1. 功能表列
  2. \n' + + '
  3. 各個工具列群組
  4. \n' + + '
  5. 側邊欄
  6. \n' + + '
  7. 頁尾中的元素路徑
  8. \n' + + '
  9. 頁尾中字數切換按鈕
  10. \n' + + '
  11. 頁尾中的品牌連結
  12. \n' + + '
  13. 頁尾中編輯器調整大小控點
  14. \n' + + '
\n' + + '\n' + + '

如果 UI 區段未顯示,表示已略過該區段。

\n' + + '\n' + + '

如果鍵盤瀏覽跳至頁尾,但沒有顯示側邊欄,則按下 Shift+Tab\n' + + ' 會跳至第一個工具列群組,而不是最後一個。

\n' + + '\n' + + '

在 UI 區段之內瀏覽

\n' + + '\n' + + '

在兩個 UI 元素之間移動,請按適當的方向鍵。

\n' + + '\n' + + '

向左向右方向鍵

\n' + + '\n' + + '
    \n' + + '
  • 在功能表列中的功能表之間移動。
  • \n' + + '
  • 開啟功能表中的子功能表。
  • \n' + + '
  • 在工具列群組中的按鈕之間移動。
  • \n' + + '
  • 在頁尾的元素路徑中項目之間移動。
  • \n' + + '
\n' + + '\n' + + '

向下向上方向鍵

\n' + + '\n' + + '
    \n' + + '
  • 在功能表中的功能表項目之間移動。
  • \n' + + '
  • 在工具列快顯功能表中的項目之間移動。
  • \n' + + '
\n' + + '\n' + + '

方向鍵會在所跳至 UI 區段之內循環。

\n' + + '\n' + + '

若要關閉已開啟的功能表、已開啟的子功能表,或已開啟的快顯功能表,請按 Esc 鍵。

\n' + + '\n' + + '

如果目前已跳至特定 UI 區段的「頂端」,則按 Esc 鍵也會結束\n' + + ' 整個鍵盤瀏覽。

\n' + + '\n' + + '

執行功能表列項目或工具列按鈕

\n' + + '\n' + + '

當想要的功能表項目或工具列按鈕已反白顯示時,按 ReturnEnter、\n' + + ' 或空白鍵即可執行該項目。

\n' + + '\n' + + '

瀏覽非索引標籤式對話方塊

\n' + + '\n' + + '

在非索引標籤式對話方塊中,開啟對話方塊時會跳至第一個互動元件。

\n' + + '\n' + + '

TabShift+Tab 即可在互動式對話方塊元件之間瀏覽。

\n' + + '\n' + + '

瀏覽索引標籤式對話方塊

\n' + + '\n' + + '

在索引標籤式對話方塊中,開啟對話方塊時會跳至索引標籤式功能表中的第一個按鈕。

\n' + + '\n' + + '

若要在此對話方塊的互動式元件之間瀏覽,請按 Tab 或\n' + + ' Shift+Tab

\n' + + '\n' + + '

先跳至索引標籤式功能表,然後按適當的方向鍵,即可切換至另一個對話方塊索引標籤,\n' + + ' 以循環瀏覽可用的索引標籤。

\n'); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/help/plugin.min.js b/apps/web-antd/public/tinymce/plugins/help/plugin.min.js new file mode 100644 index 0000000..b5d4f8d --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/help/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");let t=0;const n=e=>{const n=(new Date).getTime(),a=Math.floor(1e9*Math.random());return t++,e+"_"+a+t+String(n)},a=e=>t=>t.options.get(e),r=a("help_tabs"),o=a("forced_plugins"),i=("string",e=>"string"===(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(n=a=e,(r=String).prototype.isPrototypeOf(n)||(null===(o=a.constructor)||void 0===o?void 0:o.name)===r.name)?"string":t;var n,a,r,o})(e));const s=(void 0,e=>undefined===e);const l=e=>"function"==typeof e,m=(!1,()=>false);class c{constructor(e,t){this.tag=e,this.value=t}static some(e){return new c(!0,e)}static none(){return c.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?c.some(e(this.value)):c.none()}bind(e){return this.tag?e(this.value):c.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:c.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return null==e?c.none():c.some(e)}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}c.singletonNone=new c(!1);const u=Array.prototype.slice,p=Array.prototype.indexOf,y=(e,t)=>{const n=e.length,a=new Array(n);for(let r=0;r{const n=[];for(let a=0,r=e.length;a{const n=u.call(e,0);return n.sort(t),n},g=Object.keys,k=Object.hasOwnProperty,v=(e,t)=>k.call(e,t);var b=tinymce.util.Tools.resolve("tinymce.Resource"),f=tinymce.util.Tools.resolve("tinymce.util.I18n");const A=(e,t)=>b.load(`tinymce.html-i18n.help-keynav.${t}`,`${e}/js/i18n/keynav/${t}.js`),w=e=>A(e,f.getCode()).catch((()=>A(e,"en")));var C=tinymce.util.Tools.resolve("tinymce.Env");const M=e=>{const t=C.os.isMacOS()||C.os.isiOS(),n=t?{alt:"⌥",ctrl:"⌃",shift:"⇧",meta:"⌘",access:"⌃⌥"}:{meta:"Ctrl ",access:"Shift + Alt "},a=e.split("+"),r=y(a,(e=>{const t=e.toLowerCase().trim();return v(n,t)?n[t]:e}));return t?r.join("").replace(/\s/,""):r.join("+")},S=[{shortcuts:["Meta + B"],action:"Bold"},{shortcuts:["Meta + I"],action:"Italic"},{shortcuts:["Meta + U"],action:"Underline"},{shortcuts:["Meta + A"],action:"Select all"},{shortcuts:["Meta + Y","Meta + Shift + Z"],action:"Redo"},{shortcuts:["Meta + Z"],action:"Undo"},{shortcuts:["Access + 1"],action:"Heading 1"},{shortcuts:["Access + 2"],action:"Heading 2"},{shortcuts:["Access + 3"],action:"Heading 3"},{shortcuts:["Access + 4"],action:"Heading 4"},{shortcuts:["Access + 5"],action:"Heading 5"},{shortcuts:["Access + 6"],action:"Heading 6"},{shortcuts:["Access + 7"],action:"Paragraph"},{shortcuts:["Access + 8"],action:"Div"},{shortcuts:["Access + 9"],action:"Address"},{shortcuts:["Alt + 0"],action:"Open help dialog"},{shortcuts:["Alt + F9"],action:"Focus to menubar"},{shortcuts:["Alt + F10"],action:"Focus to toolbar"},{shortcuts:["Alt + F11"],action:"Focus to element path"},{shortcuts:["Alt + F12"],action:"Focus to notification"},{shortcuts:["Ctrl + F9"],action:"Focus to contextual toolbar"},{shortcuts:["Shift + Enter"],action:"Open popup menu for split buttons"},{shortcuts:["Meta + K"],action:"Insert link (if link plugin activated)"},{shortcuts:["Meta + S"],action:"Save (if save plugin activated)"},{shortcuts:["Meta + F"],action:"Find (if searchreplace plugin activated)"},{shortcuts:["Meta + Shift + F"],action:"Switch to or from fullscreen mode"}],_=()=>({name:"shortcuts",title:"Handy Shortcuts",items:[{type:"table",header:["Action","Shortcut"],cells:y(S,(e=>{const t=y(e.shortcuts,M).join(" or ");return[e.action,t]}))}]}),x=y([{key:"accordion",name:"Accordion"},{key:"anchor",name:"Anchor"},{key:"autolink",name:"Autolink"},{key:"autoresize",name:"Autoresize"},{key:"autosave",name:"Autosave"},{key:"charmap",name:"Character Map"},{key:"code",name:"Code"},{key:"codesample",name:"Code Sample"},{key:"colorpicker",name:"Color Picker"},{key:"directionality",name:"Directionality"},{key:"emoticons",name:"Emoticons"},{key:"fullscreen",name:"Full Screen"},{key:"help",name:"Help"},{key:"image",name:"Image"},{key:"importcss",name:"Import CSS"},{key:"insertdatetime",name:"Insert Date/Time"},{key:"link",name:"Link"},{key:"lists",name:"Lists"},{key:"advlist",name:"List Styles"},{key:"media",name:"Media"},{key:"nonbreaking",name:"Nonbreaking"},{key:"pagebreak",name:"Page Break"},{key:"preview",name:"Preview"},{key:"quickbars",name:"Quick Toolbars"},{key:"save",name:"Save"},{key:"searchreplace",name:"Search and Replace"},{key:"table",name:"Table"},{key:"textcolor",name:"Text Color"},{key:"visualblocks",name:"Visual Blocks"},{key:"visualchars",name:"Visual Characters"},{key:"wordcount",name:"Word Count"},{key:"a11ychecker",name:"Accessibility Checker",type:"premium"},{key:"typography",name:"Advanced Typography",type:"premium",slug:"advanced-typography"},{key:"ai",name:"AI Assistant",type:"premium"},{key:"casechange",name:"Case Change",type:"premium"},{key:"checklist",name:"Checklist",type:"premium"},{key:"advcode",name:"Enhanced Code Editor",type:"premium"},{key:"mediaembed",name:"Enhanced Media Embed",type:"premium",slug:"introduction-to-mediaembed"},{key:"advtable",name:"Enhanced Tables",type:"premium"},{key:"exportpdf",name:"Export to PDF",type:"premium"},{key:"exportword",name:"Export to Word",type:"premium"},{key:"footnotes",name:"Footnotes",type:"premium"},{key:"formatpainter",name:"Format Painter",type:"premium"},{key:"editimage",name:"Image Editing",type:"premium"},{key:"importword",name:"Import from Word",type:"premium"},{key:"inlinecss",name:"Inline CSS",type:"premium",slug:"inline-css"},{key:"linkchecker",name:"Link Checker",type:"premium"},{key:"math",name:"Math",type:"premium"},{key:"markdown",name:"Markdown",type:"premium"},{key:"mentions",name:"Mentions",type:"premium"},{key:"mergetags",name:"Merge Tags",type:"premium"},{key:"pageembed",name:"Page Embed",type:"premium"},{key:"permanentpen",name:"Permanent Pen",type:"premium"},{key:"powerpaste",name:"PowerPaste",type:"premium",slug:"introduction-to-powerpaste"},{key:"revisionhistory",name:"Revision History",type:"premium"},{key:"tinymcespellchecker",name:"Spell Checker",type:"premium",slug:"introduction-to-tiny-spellchecker"},{key:"autocorrect",name:"Spelling Autocorrect",type:"premium"},{key:"tableofcontents",name:"Table of Contents",type:"premium"},{key:"advtemplate",name:"Templates",type:"premium",slug:"advanced-templates"},{key:"tinycomments",name:"Tiny Comments",type:"premium",slug:"introduction-to-tiny-comments"},{key:"tinydrive",name:"Tiny Drive",type:"premium",slug:"tinydrive-introduction"}],(e=>({...e,type:e.type||"opensource",slug:e.slug||e.key}))),T=e=>{const t=e=>`${e.name}`,n=(e,n)=>{return(a=x,r=e=>e.key===n,((e,t,n)=>{for(let a=0,r=e.length;a((e,n)=>{const a=e.plugins[n].getMetadata;if(l(a)){const e=a();return{name:e.name,html:t(e)}}return{name:n,html:n}})(e,n)),(e=>{const n="premium"===e.type?`${e.name}*`:e.name;return{name:n,html:t({name:n,url:`https://www.tiny.cloud/docs/tinymce/7/${e.slug}/`})}}));var a,r},a=e=>{const t=(e=>{const t=g(e.plugins),n=o(e);return s(n)?t:h(t,(e=>!(((e,t)=>p.call(e,t))(n,e)>-1)))})(e),a=d(y(t,(t=>n(e,t))),((e,t)=>e.name.localeCompare(t.name))),r=y(a,(e=>"
  • "+e.html+"
  • ")),i=r.length,l=r.join("");return"

    "+f.translate(["Plugins installed ({0}):",i])+"

      "+l+"
    "},r={type:"htmlpanel",presets:"document",html:[(e=>null==e?"":"
    "+a(e)+"
    ")(e),(()=>{const e=h(x,(({type:e})=>"premium"===e)),t=d(y(e,(e=>e.name)),((e,t)=>e.localeCompare(t))),n=y(t,(e=>`
  • ${e}
  • `)).join("");return"

    "+f.translate("Premium plugins:")+"

    "})()].join("")};return{name:"plugins",title:"Plugins",items:[r]}};var O=tinymce.util.Tools.resolve("tinymce.EditorManager");const F=(e,t,a)=>()=>{(async(e,t,a)=>{const o=_(),s=await(async e=>({name:"keyboardnav",title:"Keyboard Navigation",items:[{type:"htmlpanel",presets:"document",html:await w(e)}]}))(a),l=T(e),m=(()=>{var e,t;const n='TinyMCE '+(e=O.majorVersion,t=O.minorVersion,(0===e.indexOf("@")?"X.X.X":e+"."+t)+"");return{name:"versions",title:"Version",items:[{type:"htmlpanel",html:"

    "+f.translate(["You are using {0}",n])+"

    ",presets:"document"}]}})(),u={[o.name]:o,[s.name]:s,[l.name]:l,[m.name]:m,...t.get()};return c.from(r(e)).fold((()=>(e=>{const t=g(e),n=t.indexOf("versions");return-1!==n&&(t.splice(n,1),t.push("versions")),{tabs:e,names:t}})(u)),(e=>((e,t)=>{const a={},r=y(e,(e=>{var r;if(i(e))return v(t,e)&&(a[e]=t[e]),e;{const t=null!==(r=e.name)&&void 0!==r?r:n("tab-name");return a[t]=e,t}}));return{tabs:a,names:r}})(e,u)))})(e,t,a).then((({tabs:t,names:n})=>{const a={type:"tabpanel",tabs:(e=>{const t=[],n=e=>{t.push(e)};for(let t=0;t{return v(n=t,a=e)?c.from(n[a]):c.none();var n,a})))};e.windowManager.open({title:"Help",size:"medium",body:a,buttons:[{type:"cancel",name:"close",text:"Close",primary:!0}],initialData:{}})}))};e.add("help",((e,t)=>{const a=(e=>{let t={};return{get:()=>t,set:e=>{t=e}}})(),r=(e=>({addTab:t=>{var a;const r=null!==(a=t.name)&&void 0!==a?a:n("tab-name"),o=e.get();o[r]=t,e.set(o)}}))(a);(e=>{(0,e.options.register)("help_tabs",{processor:"array"})})(e);const o=F(e,a,t);return((e,t)=>{e.ui.registry.addButton("help",{icon:"help",tooltip:"Help",onAction:t}),e.ui.registry.addMenuItem("help",{text:"Help",icon:"help",shortcut:"Alt+0",onAction:t})})(e,o),((e,t)=>{e.addCommand("mceHelp",t)})(e,o),e.shortcuts.add("Alt+0","Open help dialog","mceHelp"),((e,t)=>{e.on("init",(()=>{w(t)}))})(e,t),r}))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/image/plugin.min.js b/apps/web-antd/public/tinymce/plugins/image/plugin.min.js new file mode 100644 index 0000000..87959cc --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/image/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=Object.getPrototypeOf,a=(e,t,a)=>{var i;return!!a(e,t.prototype)||(null===(i=e.constructor)||void 0===i?void 0:i.name)===t.name},i=e=>t=>(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&a(e,String,((e,t)=>t.isPrototypeOf(e)))?"string":t})(t)===e,s=e=>t=>typeof t===e,r=i("string"),o=i("object"),n=e=>((e,i)=>o(e)&&a(e,i,((e,a)=>t(e)===a)))(e,Object),l=i("array"),c=(null,e=>null===e);const m=s("boolean"),d=e=>!(e=>null==e)(e),g=s("function"),u=s("number"),p=()=>{};class h{constructor(e,t){this.tag=e,this.value=t}static some(e){return new h(!0,e)}static none(){return h.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?h.some(e(this.value)):h.none()}bind(e){return this.tag?e(this.value):h.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:h.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return d(e)?h.some(e):h.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}h.singletonNone=new h(!1);const b=Object.keys,v=Object.hasOwnProperty,y=(e,t)=>v.call(e,t),f=Array.prototype.push,w=e=>{const t=[];for(let a=0,i=e.length;a{((e,t,a)=>{if(!(r(a)||m(a)||u(a)))throw console.error("Invalid call to Attribute.set. Key ",t,":: Value ",a,":: Element ",e),new Error("Attribute value was not simple");e.setAttribute(t,a+"")})(e.dom,t,a)},D=e=>{if(null==e)throw new Error("Node cannot be null or undefined");return{dom:e}},_=D;var C=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),I=tinymce.util.Tools.resolve("tinymce.util.URI");const U=e=>e.length>0,x=e=>t=>t.options.get(e),S=x("image_dimensions"),N=x("image_advtab"),T=x("image_uploadtab"),O=x("image_prepend_url"),E=x("image_class_list"),L=x("image_description"),j=x("image_title"),M=x("image_caption"),R=x("image_list"),k=x("a11y_advanced_options"),z=x("automatic_uploads"),B=(e,t)=>Math.max(parseInt(e,10),parseInt(t,10)),P=e=>(e&&(e=e.replace(/px$/,"")),e),F=e=>(e.length>0&&/^[0-9]+$/.test(e)&&(e+="px"),e),H=e=>"IMG"===e.nodeName&&(e.hasAttribute("data-mce-object")||e.hasAttribute("data-mce-placeholder")),G=(e,t)=>{const a=e.options.get;return I.isDomSafe(t,"img",{allow_html_data_urls:a("allow_html_data_urls"),allow_script_urls:a("allow_script_urls"),allow_svg_data_urls:a("allow_svg_data_urls")})},W=C.DOM,$=e=>e.style.marginLeft&&e.style.marginRight&&e.style.marginLeft===e.style.marginRight?P(e.style.marginLeft):"",V=e=>e.style.marginTop&&e.style.marginBottom&&e.style.marginTop===e.style.marginBottom?P(e.style.marginTop):"",K=e=>e.style.borderWidth?P(e.style.borderWidth):"",Z=(e,t)=>{var a;return e.hasAttribute(t)&&null!==(a=e.getAttribute(t))&&void 0!==a?a:""},q=e=>null!==e.parentNode&&"FIGURE"===e.parentNode.nodeName,J=(e,t,a)=>{""===a||null===a?e.removeAttribute(t):e.setAttribute(t,a)},Q=(e,t)=>{const a=e.getAttribute("style"),i=t(null!==a?a:"");i.length>0?(e.setAttribute("style",i),e.setAttribute("data-mce-style",i)):e.removeAttribute("style")},X=(e,t)=>(e,a,i)=>{const s=e.style;s[a]?(s[a]=F(i),Q(e,t)):J(e,a,i)},Y=(e,t)=>e.style[t]?P(e.style[t]):Z(e,t),ee=(e,t)=>{const a=F(t);e.style.marginLeft=a,e.style.marginRight=a},te=(e,t)=>{const a=F(t);e.style.marginTop=a,e.style.marginBottom=a},ae=(e,t)=>{const a=F(t);e.style.borderWidth=a},ie=(e,t)=>{e.style.borderStyle=t},se=e=>{var t;return null!==(t=e.style.borderStyle)&&void 0!==t?t:""},re=e=>d(e)&&"FIGURE"===e.nodeName,oe=e=>0===W.getAttrib(e,"alt").length&&"presentation"===W.getAttrib(e,"role"),ne=e=>oe(e)?"":Z(e,"alt"),le=(e,t)=>{var a;const i=document.createElement("img");return J(i,"style",t.style),($(i)||""!==t.hspace)&&ee(i,t.hspace),(V(i)||""!==t.vspace)&&te(i,t.vspace),(K(i)||""!==t.border)&&ae(i,t.border),(se(i)||""!==t.borderStyle)&&ie(i,t.borderStyle),e(null!==(a=i.getAttribute("style"))&&void 0!==a?a:"")},ce=(e,t)=>({src:Z(t,"src"),alt:ne(t),title:Z(t,"title"),width:Y(t,"width"),height:Y(t,"height"),class:Z(t,"class"),style:e(Z(t,"style")),caption:q(t),hspace:$(t),vspace:V(t),border:K(t),borderStyle:se(t),isDecorative:oe(t)}),me=(e,t,a,i,s)=>{a[i]!==t[i]&&s(e,i,String(a[i]))},de=(e,t,a)=>{if(a){W.setAttrib(e,"role","presentation");const t=_(e);A(t,"alt","")}else{if(c(t)){"alt",_(e).dom.removeAttribute("alt")}else{const a=_(e);A(a,"alt",t)}"presentation"===W.getAttrib(e,"role")&&W.setAttrib(e,"role","")}},ge=(e,t)=>(a,i,s)=>{e(a,s),Q(a,t)},ue=(e,t,a)=>{const i=ce(e,a);me(a,i,t,"caption",((e,t,a)=>(e=>{q(e)?(e=>{const t=e.parentNode;d(t)&&(W.insertAfter(e,t),W.remove(t))})(e):(e=>{const t=W.create("figure",{class:"image"});W.insertAfter(t,e),t.appendChild(e),t.appendChild(W.create("figcaption",{contentEditable:"true"},"Caption")),t.contentEditable="false"})(e)})(e))),me(a,i,t,"src",J),me(a,i,t,"title",J),me(a,i,t,"width",X(0,e)),me(a,i,t,"height",X(0,e)),me(a,i,t,"class",J),me(a,i,t,"style",ge(((e,t)=>J(e,"style",t)),e)),me(a,i,t,"hspace",ge(ee,e)),me(a,i,t,"vspace",ge(te,e)),me(a,i,t,"border",ge(ae,e)),me(a,i,t,"borderStyle",ge(ie,e)),((e,t,a)=>{a.alt===t.alt&&a.isDecorative===t.isDecorative||de(e,a.alt,a.isDecorative)})(a,i,t)},pe=(e,t)=>{const a=(e=>{if(e.margin){const t=String(e.margin).split(" ");switch(t.length){case 1:e["margin-top"]=e["margin-top"]||t[0],e["margin-right"]=e["margin-right"]||t[0],e["margin-bottom"]=e["margin-bottom"]||t[0],e["margin-left"]=e["margin-left"]||t[0];break;case 2:e["margin-top"]=e["margin-top"]||t[0],e["margin-right"]=e["margin-right"]||t[1],e["margin-bottom"]=e["margin-bottom"]||t[0],e["margin-left"]=e["margin-left"]||t[1];break;case 3:e["margin-top"]=e["margin-top"]||t[0],e["margin-right"]=e["margin-right"]||t[1],e["margin-bottom"]=e["margin-bottom"]||t[2],e["margin-left"]=e["margin-left"]||t[1];break;case 4:e["margin-top"]=e["margin-top"]||t[0],e["margin-right"]=e["margin-right"]||t[1],e["margin-bottom"]=e["margin-bottom"]||t[2],e["margin-left"]=e["margin-left"]||t[3]}delete e.margin}return e})(e.dom.styles.parse(t)),i=e.dom.styles.parse(e.dom.styles.serialize(a));return e.dom.styles.serialize(i)},he=e=>{const t=e.selection.getNode(),a=e.dom.getParent(t,"figure.image");return a?e.dom.select("img",a)[0]:t&&("IMG"!==t.nodeName||H(t))?null:t},be=(e,t)=>{var a;const i=e.dom,s=((t,a)=>{const i={};var s;return((e,t,a,i)=>{((e,t)=>{const a=b(e);for(let i=0,s=a.length;i{(t(e,s)?a:i)(e,s)}))})(t,((t,a)=>!e.schema.isValidChild(a,"figure")),(s=i,(e,t)=>{s[t]=e}),p),i})(e.schema.getTextBlockElements()),r=i.getParent(t.parentNode,(e=>{return t=s,a=e.nodeName,y(t,a)&&void 0!==t[a]&&null!==t[a];var t,a}),e.getBody());return r&&null!==(a=i.split(r,t))&&void 0!==a?a:t},ve=(e,t)=>{const a=((t,a)=>{const i=document.createElement("img");if(ue((t=>pe(e,t)),{...a,caption:!1},i),de(i,a.alt,a.isDecorative),a.caption){const e=W.create("figure",{class:"image"});return e.appendChild(i),e.appendChild(W.create("figcaption",{contentEditable:"true"},"Caption")),e.contentEditable="false",e}return i})(0,t);e.dom.setAttrib(a,"data-mce-id","__mcenew"),e.focus(),e.selection.setContent(a.outerHTML);const i=e.dom.select('*[data-mce-id="__mcenew"]')[0];if(e.dom.setAttrib(i,"data-mce-id",null),re(i)){const t=be(e,i);e.selection.select(t)}else e.selection.select(i)},ye=(e,t)=>{const a=he(e);if(a){const i={...ce((t=>pe(e,t)),a),...t},s=((e,t)=>{const a=t.src;return{...t,src:G(e,a)?a:""}})(e,i);i.src?((e,t)=>{const a=he(e);if(a)if(ue((t=>pe(e,t)),t,a),((e,t)=>{e.dom.setAttrib(t,"src",t.getAttribute("src"))})(e,a),re(a.parentNode)){const t=a.parentNode;be(e,t),e.selection.select(a.parentNode)}else e.selection.select(a),((e,t,a)=>{const i=()=>{a.onload=a.onerror=null,e.selection&&(e.selection.select(a),e.nodeChanged())};a.onload=()=>{t.width||t.height||!S(e)||e.dom.setAttribs(a,{width:String(a.clientWidth),height:String(a.clientHeight)}),i()},a.onerror=i})(e,t,a)})(e,s):((e,t)=>{if(t){const a=e.dom.is(t.parentNode,"figure.image")?t.parentNode:t;e.dom.remove(a),e.focus(),e.nodeChanged(),e.dom.isEmpty(e.getBody())&&(e.setContent(""),e.selection.setCursorLocation())}})(e,a)}else t.src&&ve(e,{src:"",alt:"",title:"",width:"",height:"",class:"",style:"",caption:!1,hspace:"",vspace:"",border:"",borderStyle:"",isDecorative:!1,...t})},fe=(we=(e,t)=>n(e)&&n(t)?fe(e,t):t,(...e)=>{if(0===e.length)throw new Error("Can't merge zero objects");const t={};for(let a=0;ar(e.value)?e.value:"",Ce=(e,t)=>{const a=[];return De.each(e,(e=>{const i=(e=>r(e.text)?e.text:r(e.title)?e.title:"")(e);if(void 0!==e.menu){const s=Ce(e.menu,t);a.push({text:i,items:s})}else{const s=t(e);a.push({text:i,value:s})}})),a},Ie=(e=_e)=>t=>t?h.from(t).map((t=>Ce(t,e))):h.none(),Ue=(e,t)=>((e,a)=>{for(let a=0;ay(e,"items"))(i=e[a])?Ue(i.items,t):i.value===t?h.some(i):h.none();if(s.isSome())return s}var i;return h.none()})(e),xe=Ie,Se=(e,t)=>e.bind((e=>Ue(e,t))),Ne=e=>{const t=xe((t=>e.convertURL(t.value||t.url||"","src"))),a=new Promise((a=>{((e,t)=>{const a=R(e);r(a)?fetch(a).then((e=>{e.ok&&e.json().then(t)})):g(a)?a(t):t(a)})(e,(e=>{a(t(e).map((e=>w([[{text:"None",value:""}],e]))))}))})),i=(A=E(e),Ie(_e)(A)),s=N(e),o=T(e),n=(e=>U(e.options.get("images_upload_url")))(e),l=(e=>d(e.options.get("images_upload_handler")))(e),c=(e=>{const t=he(e);return t?ce((t=>pe(e,t)),t):{src:"",alt:"",title:"",width:"",height:"",class:"",style:"",caption:!1,hspace:"",vspace:"",border:"",borderStyle:"",isDecorative:!1}})(e),m=L(e),u=j(e),p=S(e),b=M(e),v=k(e),y=z(e),f=h.some(O(e)).filter((e=>r(e)&&e.length>0));var A;return a.then((e=>({image:c,imageList:e,classList:i,hasAdvTab:s,hasUploadTab:o,hasUploadUrl:n,hasUploadHandler:l,hasDescription:m,hasImageTitle:u,hasDimensions:p,hasImageCaption:b,prependURL:f,hasAccessibilityOptions:v,automaticUploads:y})))},Te=e=>{const t=e.imageList.map((e=>({name:"images",type:"listbox",label:"Image list",items:e}))),a={name:"alt",type:"input",label:"Alternative description",enabled:!(e.hasAccessibilityOptions&&e.image.isDecorative)},i=e.classList.map((e=>({name:"classes",type:"listbox",label:"Class",items:e})));return w([[{name:"src",type:"urlinput",filetype:"image",label:"Source",picker_text:"Browse files"}],t.toArray(),e.hasAccessibilityOptions&&e.hasDescription?[{type:"label",label:"Accessibility",items:[{name:"isDecorative",type:"checkbox",label:"Image is decorative"}]}]:[],e.hasDescription?[a]:[],e.hasImageTitle?[{name:"title",type:"input",label:"Image title"}]:[],e.hasDimensions?[{name:"dimensions",type:"sizeinput"}]:[],[{...(s=e.classList.isSome()&&e.hasImageCaption,s?{type:"grid",columns:2}:{type:"panel"}),items:w([i.toArray(),e.hasImageCaption?[{type:"label",label:"Caption",items:[{type:"checkbox",name:"caption",label:"Show caption"}]}]:[]])}]]);var s},Oe=e=>({title:"General",name:"general",items:Te(e)}),Ee=Te,Le=e=>({src:{value:e.src,meta:{}},images:e.src,alt:e.alt,title:e.title,dimensions:{width:e.width,height:e.height},classes:e.class,caption:e.caption,style:e.style,vspace:e.vspace,border:e.border,hspace:e.hspace,borderstyle:e.borderStyle,fileinput:[],isDecorative:e.isDecorative}),je=(e,t)=>({src:e.src.value,alt:null!==e.alt&&0!==e.alt.length||!t?e.alt:null,title:e.title,width:e.dimensions.width,height:e.dimensions.height,class:e.classes,style:e.style,caption:e.caption,hspace:e.hspace,vspace:e.vspace,border:e.border,borderStyle:e.borderstyle,isDecorative:e.isDecorative}),Me=(e,t,a,i)=>{((e,t)=>{const a=t.getData();((e,t)=>/^(?:[a-zA-Z]+:)?\/\//.test(t)?h.none():e.prependURL.bind((e=>t.substring(0,e.length)!==e?h.some(e+t):h.none())))(e,a.src.value).each((e=>{t.setData({src:{value:e,meta:a.src.meta}})}))})(t,i),((e,t)=>{const a=t.getData(),i=a.src.meta;if(void 0!==i){const s=fe({},a);((e,t,a)=>{e.hasDescription&&r(a.alt)&&(t.alt=a.alt),e.hasAccessibilityOptions&&(t.isDecorative=a.isDecorative||t.isDecorative||!1),e.hasImageTitle&&r(a.title)&&(t.title=a.title),e.hasDimensions&&(r(a.width)&&(t.dimensions.width=a.width),r(a.height)&&(t.dimensions.height=a.height)),r(a.class)&&Se(e.classList,a.class).each((e=>{t.classes=e.value})),e.hasImageCaption&&m(a.caption)&&(t.caption=a.caption),e.hasAdvTab&&(r(a.style)&&(t.style=a.style),r(a.vspace)&&(t.vspace=a.vspace),r(a.border)&&(t.border=a.border),r(a.hspace)&&(t.hspace=a.hspace),r(a.borderstyle)&&(t.borderstyle=a.borderstyle))})(e,s,i),t.setData(s)}})(t,i),((e,t,a,i)=>{const s=i.getData(),r=s.src.value,o=s.src.meta||{};o.width||o.height||!t.hasDimensions||(U(r)?e.imageSize(r).then((e=>{a.open&&i.setData({dimensions:e})})).catch((e=>console.error(e))):i.setData({dimensions:{width:"",height:""}}))})(e,t,a,i),((e,t,a)=>{const i=a.getData(),s=Se(e.imageList,i.src.value);t.prevImage=s,a.setData({images:s.map((e=>e.value)).getOr("")})})(t,a,i)},Re=(e,t,a,i)=>{const s=i.getData();var r;i.block("Uploading image"),(r=s.fileinput,((e,t)=>0{i.unblock()}),(s=>{const r=URL.createObjectURL(s),o=()=>{i.unblock(),URL.revokeObjectURL(r)},n=s=>{i.setData({src:{value:s,meta:{}}}),i.showTab("general"),Me(e,t,a,i),i.focus("src")};var l;(l=s,new Promise(((e,t)=>{const a=new FileReader;a.onload=()=>{e(a.result)},a.onerror=()=>{var e;t(null===(e=a.error)||void 0===e?void 0:e.message)},a.readAsDataURL(l)}))).then((a=>{const l=e.createBlobCache(s,r,a);t.automaticUploads?e.uploadImage(l).then((e=>{n(e.url),o()})).catch((t=>{o(),e.alertErr(t)})):(e.addToBlobCache(l),n(l.blobUri()),i.unblock())}))}))},ke=(e,t,a)=>(i,s)=>{"src"===s.name?Me(e,t,a,i):"images"===s.name?((e,t,a,i)=>{const s=i.getData(),r=Se(t.imageList,s.images);r.each((e=>{const t=""===s.alt||a.prevImage.map((e=>e.text===s.alt)).getOr(!1);t?""===e.value?i.setData({src:e,alt:a.prevAlt}):i.setData({src:e,alt:e.text}):i.setData({src:e})})),a.prevImage=r,Me(e,t,a,i)})(e,t,a,i):"alt"===s.name?a.prevAlt=i.getData().alt:"fileinput"===s.name?Re(e,t,a,i):"isDecorative"===s.name&&i.setEnabled("alt",!i.getData().isDecorative)},ze=e=>()=>{e.open=!1},Be=e=>e.hasAdvTab||e.hasUploadUrl||e.hasUploadHandler?{type:"tabpanel",tabs:w([[Oe(e)],e.hasAdvTab?[{title:"Advanced",name:"advanced",items:[{type:"grid",columns:2,items:[{type:"input",label:"Vertical space",name:"vspace",inputMode:"numeric"},{type:"input",label:"Horizontal space",name:"hspace",inputMode:"numeric"},{type:"input",label:"Border width",name:"border",inputMode:"numeric"},{type:"listbox",name:"borderstyle",label:"Border style",items:[{text:"Select...",value:""},{text:"Solid",value:"solid"},{text:"Dotted",value:"dotted"},{text:"Dashed",value:"dashed"},{text:"Double",value:"double"},{text:"Groove",value:"groove"},{text:"Ridge",value:"ridge"},{text:"Inset",value:"inset"},{text:"Outset",value:"outset"},{text:"None",value:"none"},{text:"Hidden",value:"hidden"}]}]}]}]:[],e.hasUploadTab&&(e.hasUploadUrl||e.hasUploadHandler)?[{title:"Upload",name:"upload",items:[{type:"dropzone",name:"fileinput"}]}]:[]])}:{type:"panel",items:Ee(e)},Pe=(e,t,a)=>i=>{const s=fe(Le(t.image),i.getData()),r={...s,style:le(a.normalizeCss,je(s,!1))};e.execCommand("mceUpdateImage",!1,je(r,t.hasAccessibilityOptions)),e.editorUpload.uploadImagesAuto(),i.close()},Fe=e=>t=>G(e,t)?(e=>new Promise((t=>{const a=document.createElement("img"),i=e=>{a.onload=a.onerror=null,a.parentNode&&a.parentNode.removeChild(a),t(e)};a.onload=()=>{const e={width:B(a.width,a.clientWidth),height:B(a.height,a.clientHeight)};i(Promise.resolve(e))},a.onerror=()=>{i(Promise.reject(`Failed to get image dimensions for: ${e}`))};const s=a.style;s.visibility="hidden",s.position="fixed",s.bottom=s.left="0px",s.width=s.height="auto",document.body.appendChild(a),a.src=e})))(e.documentBaseURI.toAbsolute(t)).then((e=>({width:String(e.width),height:String(e.height)}))):Promise.resolve({width:"",height:""}),He=e=>(t,a,i)=>{var s;return e.editorUpload.blobCache.create({blob:t,blobUri:a,name:null===(s=t.name)||void 0===s?void 0:s.replace(/\.[^\.]+$/,""),filename:t.name,base64:i.split(",")[1]})},Ge=e=>t=>{e.editorUpload.blobCache.add(t)},We=e=>t=>{e.windowManager.alert(t)},$e=e=>t=>pe(e,t),Ve=e=>t=>e.dom.parseStyle(t),Ke=e=>(t,a)=>e.dom.serializeStyle(t,a),Ze=e=>t=>Ae(e).upload([t],!1).then((e=>{var t;return 0===e.length?Promise.reject("Failed to upload image"):!1===e[0].status?Promise.reject(null===(t=e[0].error)||void 0===t?void 0:t.message):e[0]})),qe=e=>{const t={imageSize:Fe(e),addToBlobCache:Ge(e),createBlobCache:He(e),alertErr:We(e),normalizeCss:$e(e),parseStyle:Ve(e),serializeStyle:Ke(e),uploadImage:Ze(e)};return{open:()=>{Ne(e).then((a=>{const i=(e=>({prevImage:Se(e.imageList,e.image.src),prevAlt:e.image.alt,open:!0}))(a);return{title:"Insert/Edit Image",size:"normal",body:Be(a),buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],initialData:Le(a.image),onSubmit:Pe(e,a,t),onChange:ke(t,a,i),onClose:ze(i)}})).then(e.windowManager.open)}}},Je=e=>{const t=e.attr("class");return d(t)&&/\bimage\b/.test(t)},Qe=e=>t=>{let a=t.length;const i=t=>{t.attr("contenteditable",e?"true":null)};for(;a--;){const s=t[a];Je(s)&&(s.attr("contenteditable",e?"false":null),De.each(s.getAll("figcaption"),i))}},Xe=e=>t=>{const a=()=>{t.setEnabled(e.selection.isEditable())};return e.on("NodeChange",a),a(),()=>{e.off("NodeChange",a)}};e.add("image",(e=>{(e=>{const t=e.options.register;t("image_dimensions",{processor:"boolean",default:!0}),t("image_advtab",{processor:"boolean",default:!1}),t("image_uploadtab",{processor:"boolean",default:!0}),t("image_prepend_url",{processor:"string",default:""}),t("image_class_list",{processor:"object[]"}),t("image_description",{processor:"boolean",default:!0}),t("image_title",{processor:"boolean",default:!1}),t("image_caption",{processor:"boolean",default:!1}),t("image_list",{processor:e=>{const t=!1===e||r(e)||((e,t)=>{if(l(e)){for(let a=0,i=e.length;a{e.on("PreInit",(()=>{e.parser.addNodeFilter("figure",Qe(!0)),e.serializer.addNodeFilter("figure",Qe(!1))}))})(e),(e=>{e.ui.registry.addToggleButton("image",{icon:"image",tooltip:"Insert/edit image",onAction:qe(e).open,onSetup:t=>{t.setActive(d(he(e)));const a=e.selection.selectorChangedWithUnbind("img:not([data-mce-object]):not([data-mce-placeholder]),figure.image",t.setActive).unbind,i=Xe(e)(t);return()=>{a(),i()}}}),e.ui.registry.addMenuItem("image",{icon:"image",text:"Image...",onAction:qe(e).open,onSetup:Xe(e)}),e.ui.registry.addContextMenu("image",{update:t=>e.selection.isEditable()&&(re(t)||"IMG"===t.nodeName&&!H(t))?["image"]:[]})})(e),(e=>{e.addCommand("mceImage",qe(e).open),e.addCommand("mceUpdateImage",((t,a)=>{e.undoManager.transact((()=>ye(e,a)))}))})(e)}))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/importcss/plugin.min.js b/apps/web-antd/public/tinymce/plugins/importcss/plugin.min.js new file mode 100644 index 0000000..a59099b --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/importcss/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>t=>(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(s=r=e,(o=String).prototype.isPrototypeOf(s)||(null===(n=r.constructor)||void 0===n?void 0:n.name)===o.name)?"string":t;var s,r,o,n})(t)===e,s=t("string"),r=t("object"),o=t("array"),n=("function",e=>"function"==typeof e);var c=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),i=tinymce.util.Tools.resolve("tinymce.EditorManager"),l=tinymce.util.Tools.resolve("tinymce.Env"),a=tinymce.util.Tools.resolve("tinymce.util.Tools");const p=e=>t=>t.options.get(e),u=p("importcss_merge_classes"),m=p("importcss_exclusive"),f=p("importcss_selector_converter"),y=p("importcss_selector_filter"),d=p("importcss_groups"),h=p("importcss_append"),g=p("importcss_file_filter"),_=p("skin"),v=p("skin_url"),b=Array.prototype.push,x=/^\.(?:ephox|tiny-pageembed|mce)(?:[.-]+\w+)+$/,T=e=>s(e)?t=>-1!==t.indexOf(e):e instanceof RegExp?t=>e.test(t):e,S=(e,t)=>{let s={};const r=/^(?:([a-z0-9\-_]+))?(\.[a-z0-9_\-\.]+)$/i.exec(t);if(!r)return;const o=r[1],n=r[2].substr(1).split(".").join(" "),c=a.makeMap("a,img");return r[1]?(s={title:t},e.schema.getTextBlockElements()[o]?s.block=o:e.schema.getBlockElements()[o]||c[o.toLowerCase()]?s.selector=o:s.inline=o):r[2]&&(s={inline:"span",title:t.substr(1),classes:n}),u(e)?s.classes=n:s.attributes={class:n},s},k=(e,t)=>null===t||m(e),M=e=>{e.on("init",(()=>{const t=(()=>{const e=[],t=[],s={};return{addItemToGroup:(e,r)=>{s[e]?s[e].push(r):(t.push(e),s[e]=[r])},addItem:t=>{e.push(t)},toFormats:()=>{return(r=t,n=e=>{const t=s[e];return 0===t.length?[]:[{title:e,items:t}]},(e=>{const t=[];for(let s=0,r=e.length;s{const s=e.length,r=new Array(s);for(let o=0;oa.map(e,(e=>a.extend({},e,{original:e,selectors:{},filter:T(e.filter)}))))(d(e)),u=(t,s)=>{if(((e,t,s,r)=>!(k(e,s)?t in r:t in s.selectors))(e,t,s,r)){((e,t,s,r)=>{k(e,s)?r[t]=!0:s.selectors[t]=!0})(e,t,s,r);const o=((e,t,s,r)=>{let o;const n=f(e);return o=r&&r.selector_converter?r.selector_converter:n||(()=>S(e,s)),o.call(t,s,r)})(e,e.plugins.importcss,t,s);if(o){const t=o.name||c.DOM.uniqueId();return e.formatter.register(t,o),{title:o.title,format:t}}}return null};a.each(((e,t,r)=>{const o=[],n={},c=(t,n)=>{let p,u=t.href;if(u=(e=>{const t=l.cacheSuffix;return s(e)&&(e=e.replace("?"+t,"").replace("&"+t,"")),e})(u),u&&(!r||r(u,n))&&!((e,t)=>{const s=_(e);if(s){const r=v(e),o=r?e.documentBaseURI.toAbsolute(r):i.baseURL+"/skins/ui/"+s,n=i.baseURL+"/skins/content/",c=e.editorManager.suffix;return t===o+"/content"+(e.inline?".inline":"")+`${c}.css`||-1!==t.indexOf(n)}return!1})(e,u)){a.each(t.imports,(e=>{c(e,!0)}));try{p=t.cssRules||t.rules}catch(e){}a.each(p,(e=>{e.styleSheet&&e.styleSheet?c(e.styleSheet,!0):e.selectorText&&a.each(e.selectorText.split(","),(e=>{o.push(a.trim(e))}))}))}};a.each(e.contentCSS,(e=>{n[e]=!0})),r||(r=(e,t)=>t||n[e]);try{a.each(t.styleSheets,(e=>{c(e)}))}catch(e){}return o})(e,e.getDoc(),T(g(e))),(e=>{if(!x.test(e)&&(!n||n(e))){const s=((e,t)=>a.grep(e,(e=>!e.filter||e.filter(t))))(p,e);if(s.length>0)a.each(s,(s=>{const r=u(e,s);r&&t.addItemToGroup(s.title,r)}));else{const s=u(e,null);s&&t.addItem(s)}}}));const m=t.toFormats();e.dispatch("addStyleModifications",{items:m,replace:!h(e)})}))};e.add("importcss",(e=>((e=>{const t=e.options.register,o=e=>s(e)||n(e)||r(e);t("importcss_merge_classes",{processor:"boolean",default:!0}),t("importcss_exclusive",{processor:"boolean",default:!0}),t("importcss_selector_converter",{processor:"function"}),t("importcss_selector_filter",{processor:o}),t("importcss_file_filter",{processor:o}),t("importcss_groups",{processor:"object[]"}),t("importcss_append",{processor:"boolean",default:!1})})(e),M(e),(e=>({convertSelectorToFormat:t=>S(e,t)}))(e))))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/insertdatetime/plugin.min.js b/apps/web-antd/public/tinymce/plugins/insertdatetime/plugin.min.js new file mode 100644 index 0000000..06b5a09 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/insertdatetime/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>t=>t.options.get(e),a=t("insertdatetime_dateformat"),n=t("insertdatetime_timeformat"),r=t("insertdatetime_formats"),s=t("insertdatetime_element"),i="Sun Mon Tue Wed Thu Fri Sat Sun".split(" "),o="Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday".split(" "),l="Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),m="January February March April May June July August September October November December".split(" "),c=(e,t)=>{if((e=""+e).length(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=t.replace("%D","%m/%d/%Y")).replace("%r","%I:%M:%S %p")).replace("%Y",""+a.getFullYear())).replace("%y",""+a.getYear())).replace("%m",c(a.getMonth()+1,2))).replace("%d",c(a.getDate(),2))).replace("%H",""+c(a.getHours(),2))).replace("%M",""+c(a.getMinutes(),2))).replace("%S",""+c(a.getSeconds(),2))).replace("%I",""+((a.getHours()+11)%12+1))).replace("%p",a.getHours()<12?"AM":"PM")).replace("%B",""+e.translate(m[a.getMonth()]))).replace("%b",""+e.translate(l[a.getMonth()]))).replace("%A",""+e.translate(o[a.getDay()]))).replace("%a",""+e.translate(i[a.getDay()]))).replace("%%","%"),u=(e,t)=>{if(s(e)){const a=d(e,t);let n;n=/%[HMSIp]/.test(t)?d(e,"%Y-%m-%dT%H:%M"):d(e,"%Y-%m-%d");const r=e.dom.getParent(e.selection.getStart(),"time");r?((e,t,a,n)=>{const r=e.dom.create("time",{datetime:a},n);e.dom.replace(r,t),e.selection.select(r,!0),e.selection.collapse(!1)})(e,r,n,a):e.insertContent('")}else e.insertContent(d(e,t))};var p=tinymce.util.Tools.resolve("tinymce.util.Tools");const g=e=>t=>{const a=()=>{t.setEnabled(e.selection.isEditable())};return e.on("NodeChange",a),a(),()=>{e.off("NodeChange",a)}};e.add("insertdatetime",(e=>{(e=>{const t=e.options.register;t("insertdatetime_dateformat",{processor:"string",default:e.translate("%Y-%m-%d")}),t("insertdatetime_timeformat",{processor:"string",default:e.translate("%H:%M:%S")}),t("insertdatetime_formats",{processor:"string[]",default:["%H:%M:%S","%Y-%m-%d","%I:%M:%S %p","%D"]}),t("insertdatetime_element",{processor:"boolean",default:!1})})(e),(e=>{e.addCommand("mceInsertDate",((t,n)=>{u(e,null!=n?n:a(e))})),e.addCommand("mceInsertTime",((t,a)=>{u(e,null!=a?a:n(e))}))})(e),(e=>{const t=r(e),a=(e=>{let t=e;return{get:()=>t,set:e=>{t=e}}})((e=>{const t=r(e);return t.length>0?t[0]:n(e)})(e)),s=t=>e.execCommand("mceInsertDate",!1,t);e.ui.registry.addSplitButton("insertdatetime",{icon:"insert-time",tooltip:"Insert date/time",select:e=>e===a.get(),fetch:a=>{a(p.map(t,(t=>({type:"choiceitem",text:d(e,t),value:t}))))},onAction:e=>{s(a.get())},onItemAction:(e,t)=>{a.set(t),s(t)},onSetup:g(e)});const i=e=>()=>{a.set(e),s(e)};e.ui.registry.addNestedMenuItem("insertdatetime",{icon:"insert-time",text:"Date/time",getSubmenuItems:()=>p.map(t,(t=>({type:"menuitem",text:d(e,t),onAction:i(t)}))),onSetup:g(e)})})(e)}))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/link/plugin.min.js b/apps/web-antd/public/tinymce/plugins/link/plugin.min.js new file mode 100644 index 0000000..65e9efa --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/link/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>t=>(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(n=o=e,(r=String).prototype.isPrototypeOf(n)||(null===(l=o.constructor)||void 0===l?void 0:l.name)===r.name)?"string":t;var n,o,r,l})(t)===e,n=e=>t=>typeof t===e,o=t("string"),r=t("object"),l=t("array"),s=(null,e=>null===e);const a=n("boolean"),i=e=>!(e=>null==e)(e),c=n("function"),u=(e,t)=>{if(l(e)){for(let n=0,o=e.length;n{},d=(e,t)=>e===t;class m{constructor(e,t){this.tag=e,this.value=t}static some(e){return new m(!0,e)}static none(){return m.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?m.some(e(this.value)):m.none()}bind(e){return this.tag?e(this.value):m.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:m.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return i(e)?m.some(e):m.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}m.singletonNone=new m(!1);const h=Array.prototype.indexOf,p=Array.prototype.push,f=e=>{const t=[];for(let n=0,o=e.length;n{for(let n=0;ne.exists((e=>n(e,t))),x=e=>{const t=[],n=e=>{t.push(e)};for(let t=0;te?m.some(t):m.none(),b=e=>t=>t.options.get(e),_=b("link_assume_external_targets"),w=b("link_context_toolbar"),C=b("link_list"),O=b("link_default_target"),S=b("link_default_protocol"),N=b("link_target_list"),A=b("link_rel_list"),T=b("link_class_list"),E=b("link_title"),L=b("allow_unsafe_link_target"),R=b("link_quicklink"),P=Object.keys,M=Object.hasOwnProperty,D=(e,t)=>M.call(e,t);var B=tinymce.util.Tools.resolve("tinymce.util.URI"),I=tinymce.util.Tools.resolve("tinymce.dom.TreeWalker"),K=tinymce.util.Tools.resolve("tinymce.util.Tools");const j=e=>i(e)&&"a"===e.nodeName.toLowerCase(),U=e=>j(e)&&!!V(e),q=(e,t)=>{if(e.collapsed)return[];{const n=e.cloneContents(),o=n.firstChild,r=new I(o,n),l=[];let s=o;do{t(s)&&l.push(s)}while(s=r.next());return l}},F=e=>/^\w+:/i.test(e),V=e=>{var t,n;return null!==(n=null!==(t=e.getAttribute("data-mce-href"))&&void 0!==t?t:e.getAttribute("href"))&&void 0!==n?n:""},$=(e,t)=>{const n=["noopener"],o=e?e.split(/\s+/):[],r=e=>e.filter((e=>-1===K.inArray(n,e))),l=t?(e=>(e=r(e)).length>0?e.concat(n):n)(o):r(o);return l.length>0?(e=>K.trim(e.sort().join(" ")))(l):""},z=(e,t)=>(t=t||J(e.selection.getRng())[0]||e.selection.getNode(),Y(t)?m.from(e.dom.select("a[href]",t)[0]):m.from(e.dom.getParent(t,"a[href]"))),G=(e,t)=>z(e,t).isSome(),H=(e,t)=>t.fold((()=>e.getContent({format:"text"})),(e=>e.innerText||e.textContent||"")).replace(/\uFEFF/g,""),J=e=>q(e,U),W=e=>K.grep(e,U),Q=e=>W(e).length>0,X=e=>{const t=e.schema.getTextInlineElements();if(z(e).exists((e=>e.hasAttribute("data-mce-block"))))return!1;const n=e.selection.getRng();return!!n.collapsed||0===q(n,(e=>1===e.nodeType&&!j(e)&&!D(t,e.nodeName.toLowerCase()))).length},Y=e=>i(e)&&"FIGURE"===e.nodeName&&/\bimage\b/i.test(e.className),Z=(e,t,n)=>{const o=e.selection.getNode(),r=z(e,o),l=((e,t)=>{const n={...t};if(0===A(e).length&&!L(e)){const e=$(n.rel,"_blank"===n.target);n.rel=e||null}return m.from(n.target).isNone()&&!1===N(e)&&(n.target=O(e)),n.href=((e,t)=>"http"!==t&&"https"!==t||F(e)?e:t+"://"+e)(n.href,_(e)),n})(e,(e=>{return t=["title","rel","class","target"],n=(t,n)=>(e[n].each((e=>{t[n]=e.length>0?e:null})),t),o={href:e.href},((e,t)=>{for(let n=0,o=e.length;n{o=n(o,e)})),o;var t,n,o})(n));e.undoManager.transact((()=>{n.href===t.href&&t.attach(),r.fold((()=>{((e,t,n,o)=>{const r=e.dom;Y(t)?re(r,t,o):n.fold((()=>{e.execCommand("mceInsertLink",!1,o)}),(t=>{e.insertContent(r.createHTML("a",o,r.encode(t)))}))})(e,o,n.text,l)}),(t=>{e.focus(),((e,t,n,o)=>{n.each((e=>{D(t,"innerText")?t.innerText=e:t.textContent=e})),e.dom.setAttribs(t,o),e.selection.select(t)})(e,t,n.text,l)}))}))},ee=e=>{const{class:t,href:n,rel:o,target:r,text:l,title:a}=e;return((e,t)=>{const n={};var o;return((e,t,n,o)=>{((e,t)=>{const n=P(e);for(let o=0,r=n.length;o{(t(e,r)?n:o)(e,r)}))})(e,((e,t)=>!1===s(e)),(o=n,(e,t)=>{o[t]=e}),g),n})({class:t.getOrNull(),href:n,rel:o.getOrNull(),target:r.getOrNull(),text:l.getOrNull(),title:a.getOrNull()})},te=(e,t,n)=>{const o=((e,t)=>{const n=e.options.get,o={allow_html_data_urls:n("allow_html_data_urls"),allow_script_urls:n("allow_script_urls"),allow_svg_data_urls:n("allow_svg_data_urls")},r=t.href;return{...t,href:B.isDomSafe(r,"a",o)?r:""}})(e,n);e.hasPlugin("rtc",!0)?e.execCommand("createlink",!1,ee(o)):Z(e,t,o)},ne=e=>{e.hasPlugin("rtc",!0)?e.execCommand("unlink"):(e=>{e.undoManager.transact((()=>{const t=e.selection.getNode();Y(t)?oe(e,t):(e=>{const t=e.dom,n=e.selection,o=n.getBookmark(),r=n.getRng().cloneRange(),l=t.getParent(r.startContainer,"a[href]",e.getBody()),s=t.getParent(r.endContainer,"a[href]",e.getBody());l&&r.setStartBefore(l),s&&r.setEndAfter(s),n.setRng(r),e.execCommand("unlink"),n.moveToBookmark(o)})(e),e.focus()}))})(e)},oe=(e,t)=>{var n;const o=e.dom.select("img",t)[0];if(o){const r=e.dom.getParents(o,"a[href]",t)[0];r&&(null===(n=r.parentNode)||void 0===n||n.insertBefore(o,r),e.dom.remove(r))}},re=(e,t,n)=>{var o;const r=e.select("img",t)[0];if(r){const t=e.create("a",n);null===(o=r.parentNode)||void 0===o||o.insertBefore(t,r),t.appendChild(r)}},le=e=>o(e.value)?e.value:"",se=(e,t)=>{const n=[];return K.each(e,(e=>{const r=(e=>o(e.text)?e.text:o(e.title)?e.title:"")(e);if(void 0!==e.menu){const o=se(e.menu,t);n.push({text:r,items:o})}else{const o=t(e);n.push({text:r,value:o})}})),n},ae=(e=le)=>t=>m.from(t).map((t=>se(t,e))),ie=e=>ae(le)(e),ce=ae,ue=(e,t)=>n=>({name:e,type:"listbox",label:t,items:n}),ge=le,de=(e,t)=>k(t,(t=>(e=>{return D(t=e,n="items")&&void 0!==t[n]&&null!==t[n];var t,n})(t)?de(e,t.items):y(t.value===e,t))),me=(e,t)=>{const n={text:e.text,title:e.title},o=(e,o)=>{const r=(l=t,s=o,"link"===s?l.link:"anchor"===s?l.anchor:m.none()).getOr([]);var l,s;return((e,t,n,o)=>{const r=o[t],l=e.length>0;return void 0!==r?de(r,n).map((t=>({url:{value:t.value,meta:{text:l?e:t.text,attach:g}},text:l?e:t.text}))):m.none()})(n.text,o,r,e)};return{onChange:(e,t)=>{const r=t.name;return"url"===r?(e=>{const t=(o=e.url,y(n.text.length<=0,m.from(null===(r=o.meta)||void 0===r?void 0:r.text).getOr(o.value)));var o,r;const l=(e=>{var t;return y(n.title.length<=0,m.from(null===(t=e.meta)||void 0===t?void 0:t.title).getOr(""))})(e.url);return t.isSome()||l.isSome()?m.some({...t.map((e=>({text:e}))).getOr({}),...l.map((e=>({title:e}))).getOr({})}):m.none()})(e()):((e,t)=>h.call(e,t))(["anchor","link"],r)>-1?o(e(),r):"text"===r||"title"===r?(n[r]=e()[r],m.none()):m.none()}}};var he=tinymce.util.Tools.resolve("tinymce.util.Delay");const pe=e=>{const t=e.href;return t.indexOf("@")>0&&-1===t.indexOf("/")&&-1===t.indexOf("mailto:")?m.some({message:"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?",preprocess:e=>({...e,href:"mailto:"+t})}):m.none()},fe=(e,t)=>n=>{const o=n.href;return 1===e&&!F(o)||0===e&&/^\s*www(\.|\d\.)/i.test(o)?m.some({message:`The URL you entered seems to be an external link. Do you want to add the required ${t}:// prefix?`,preprocess:e=>({...e,href:t+"://"+o})}):m.none()},ke=e=>{const t=e.dom.select("a:not([href])"),n=f(((e,t)=>{const n=e.length,o=new Array(n);for(let r=0;r{const t=e.name||e.id;return t?[{text:t,value:"#"+t}]:[]})));return n.length>0?m.some([{text:"None",value:""}].concat(n)):m.none()},ve=e=>{const t=T(e);return t.length>0?ie(t):m.none()},xe=e=>{try{return m.some(JSON.parse(e))}catch(e){return m.none()}},ye=(e,t)=>{const n=A(e);if(n.length>0){const o=v(t,"_blank"),r=e=>$(ge(e),o);return(!1===L(e)?ce(r):ie)(n)}return m.none()},be=[{text:"Current window",value:""},{text:"New window",value:"_blank"}],_e=e=>{const t=N(e);return l(t)?ie(t).orThunk((()=>m.some(be))):!1===t?m.none():m.some(be)},we=(e,t,n)=>{const o=e.getAttrib(t,n);return null!==o&&o.length>0?m.some(o):m.none()},Ce=(e,t)=>(e=>{const t=t=>e.convertURL(t.value||t.url||"","href"),n=C(e);return new Promise((e=>{o(n)?fetch(n).then((e=>e.ok?e.text().then(xe):Promise.reject())).then(e,(()=>e(m.none()))):c(n)?n((t=>e(m.some(t)))):e(m.from(n))})).then((e=>e.bind(ce(t)).map((e=>e.length>0?[{text:"None",value:""}].concat(e):e))))})(e).then((n=>{const o=((e,t)=>{const n=e.dom,o=X(e)?m.some(H(e.selection,t)):m.none(),r=t.bind((e=>m.from(n.getAttrib(e,"href")))),l=t.bind((e=>m.from(n.getAttrib(e,"target")))),s=t.bind((e=>we(n,e,"rel"))),a=t.bind((e=>we(n,e,"class")));return{url:r,text:o,title:t.bind((e=>we(n,e,"title"))),target:l,rel:s,linkClass:a}})(e,t);return{anchor:o,catalogs:{targets:_e(e),rels:ye(e,o.target),classes:ve(e),anchor:ke(e),link:n},optNode:t,flags:{titleEnabled:E(e)}}})),Oe=e=>{const t=(e=>{const t=z(e);return Ce(e,t)})(e);t.then((t=>{const n=((e,t)=>n=>{const o=n.getData();if(!o.url.value)return ne(e),void n.close();const r=e=>m.from(o[e]).filter((n=>!v(t.anchor[e],n))),l={href:o.url.value,text:r("text"),target:r("target"),rel:r("rel"),class:r("linkClass"),title:r("title")},s={href:o.url.value,attach:void 0!==o.url.meta&&o.url.meta.attach?o.url.meta.attach:g};((e,t)=>k([pe,fe(_(e),S(e))],(e=>e(t))).fold((()=>Promise.resolve(t)),(n=>new Promise((o=>{((e,t,n)=>{const o=e.selection.getRng();he.setEditorTimeout(e,(()=>{e.windowManager.confirm(t,(t=>{e.selection.setRng(o),n(t)}))}))})(e,n.message,(e=>{o(e?n.preprocess(t):t)}))})))))(e,l).then((t=>{te(e,s,t)})),n.close()})(e,t);return((e,t,n)=>{const o=e.anchor.text.map((()=>({name:"text",type:"input",label:"Text to display"}))).toArray(),r=e.flags.titleEnabled?[{name:"title",type:"input",label:"Title"}]:[],l=((e,t)=>{const n=e.anchor,o=n.url.getOr("");return{url:{value:o,meta:{original:{value:o}}},text:n.text.getOr(""),title:n.title.getOr(""),anchor:o,link:o,rel:n.rel.getOr(""),target:n.target.or(t).getOr(""),linkClass:n.linkClass.getOr("")}})(e,m.from(O(n))),s=e.catalogs,a=me(l,s);return{title:"Insert/Edit Link",size:"normal",body:{type:"panel",items:f([[{name:"url",type:"urlinput",filetype:"file",label:"URL",picker_text:"Browse links"}],o,r,x([s.anchor.map(ue("anchor","Anchors")),s.rels.map(ue("rel","Rel")),s.targets.map(ue("target","Open link in...")),s.link.map(ue("link","Link list")),s.classes.map(ue("linkClass","Class"))])])},buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],initialData:l,onChange:(e,{name:t})=>{a.onChange(e.getData,{name:t}).each((t=>{e.setData(t)}))},onSubmit:t}})(t,n,e)})).then((t=>{e.windowManager.open(t)}))};var Se=tinymce.util.Tools.resolve("tinymce.util.VK");const Ne=(e,t)=>{if(t){const n=V(t);if(/^#/.test(n)){const t=e.dom.select(n);t.length&&e.selection.scrollIntoView(t[0],!0)}else(e=>{const t=document.createElement("a");t.target="_blank",t.href=e,t.rel="noreferrer noopener";const n=new MouseEvent("click",{bubbles:!0,cancelable:!0,view:window});document.dispatchEvent(n),((e,t)=>{document.body.appendChild(e),e.dispatchEvent(t),document.body.removeChild(e)})(t,n)})(t.href)}},Ae=(e,t)=>{const n=W(e.dom.getParents(t));return y(1===n.length,n[0])},Te=e=>e.selection.isCollapsed()||(e=>{const t=e.selection.getRng(),n=t.startContainer;return U(n)&&t.startContainer===t.endContainer&&1===e.dom.select("img",n).length})(e)?Ae(e,e.selection.getStart()):(e=>{const t=J(e.selection.getRng());return y(t.length>0,t[0]).or(Ae(e,e.selection.getNode()))})(e),Ee=e=>()=>{e.execCommand("mceLink",!1,{dialog:!0})},Le=(e,t)=>(e.on("NodeChange",t),()=>e.off("NodeChange",t)),Re=e=>t=>{const n=()=>{t.setActive(!e.mode.isReadOnly()&&G(e,e.selection.getNode())),t.setEnabled(e.selection.isEditable())};return n(),Le(e,n)},Pe=e=>t=>{const n=()=>{t.setEnabled(e.selection.isEditable())};return n(),Le(e,n)},Me=e=>t=>{const n=e.dom.getParents(e.selection.getStart()),o=n=>{t.setEnabled((t=>{return Q(t)||(n=e.selection.getRng(),J(n).length>0);var n})(n)&&e.selection.isEditable())};return o(n),Le(e,(e=>o(e.parents)))},De=e=>{const t=(e=>{const t=(()=>{const e=(e=>{const t=(e=>{let t=e;return{get:()=>t,set:e=>{t=e}}})(m.none()),n=()=>t.get().each(e);return{clear:()=>{n(),t.set(m.none())},isSet:()=>t.get().isSome(),get:()=>t.get(),set:e=>{n(),t.set(m.some(e))}}})(g);return{...e,on:t=>e.get().each(t)}})(),n=()=>t.get().or(Te(e));return e.on("contextmenu",(n=>{Ae(e,n.target).each(t.set)})),e.on("SelectionChange",(()=>{t.isSet()||Te(e).each(t.set)})),e.on("click",(n=>{t.clear();const o=W(e.dom.getParents(n.target));1===o.length&&Se.metaKeyPressed(n)&&(n.preventDefault(),Ne(e,o[0]))})),e.on("keydown",(o=>{t.clear(),!o.isDefaultPrevented()&&13===o.keyCode&&(e=>!0===e.altKey&&!1===e.shiftKey&&!1===e.ctrlKey&&!1===e.metaKey)(o)&&n().each((t=>{o.preventDefault(),Ne(e,t)}))})),{gotoSelectedLink:()=>n().each((t=>Ne(e,t)))}})(e);((e,t)=>{e.ui.registry.addToggleButton("link",{icon:"link",tooltip:"Insert/edit link",shortcut:"Meta+K",onAction:Ee(e),onSetup:Re(e)}),e.ui.registry.addButton("openlink",{icon:"new-tab",tooltip:"Open link",onAction:t.gotoSelectedLink,onSetup:Me(e)}),e.ui.registry.addButton("unlink",{icon:"unlink",tooltip:"Remove link",onAction:()=>ne(e),onSetup:Me(e)})})(e,t),((e,t)=>{e.ui.registry.addMenuItem("openlink",{text:"Open link",icon:"new-tab",onAction:t.gotoSelectedLink,onSetup:Me(e)}),e.ui.registry.addMenuItem("link",{icon:"link",text:"Link...",shortcut:"Meta+K",onAction:Ee(e),onSetup:Pe(e)}),e.ui.registry.addMenuItem("unlink",{icon:"unlink",text:"Remove link",onAction:()=>ne(e),onSetup:Me(e)})})(e,t),(e=>{e.ui.registry.addContextMenu("link",{update:t=>e.dom.isEditable(t)?Q(e.dom.getParents(t,"a"))?"link unlink openlink":"link":""})})(e),((e,t)=>{const n=t=>{const n=e.selection.getNode();return t.setEnabled(G(e,n)),g};e.ui.registry.addContextForm("quicklink",{launch:{type:"contextformtogglebutton",icon:"link",tooltip:"Link",onSetup:Re(e)},label:"Link",predicate:t=>w(e)&&G(e,t),initValue:()=>z(e).fold((()=>""),V),commands:[{type:"contextformtogglebutton",icon:"link",tooltip:"Link",primary:!0,onSetup:t=>{const n=e.selection.getNode();return t.setActive(G(e,n)),Re(e)(t)},onAction:t=>{const n=t.getValue(),o=(t=>{const n=z(e),o=X(e);if(n.isNone()&&o){const o=H(e.selection,n);return y(0===o.length,t)}return m.none()})(n);te(e,{href:n,attach:g},{href:n,text:o,title:m.none(),rel:m.none(),target:m.from(O(e)),class:m.none()}),(e=>{e.selection.collapse(!1)})(e),t.hide()}},{type:"contextformbutton",icon:"unlink",tooltip:"Remove link",onSetup:n,onAction:t=>{ne(e),t.hide()}},{type:"contextformbutton",icon:"new-tab",tooltip:"Open link",onSetup:n,onAction:e=>{t.gotoSelectedLink(),e.hide()}}]})})(e,t)};e.add("link",(e=>{(e=>{const t=e.options.register;t("link_assume_external_targets",{processor:e=>{const t=o(e)||a(e);return t?!0===e?{value:1,valid:t}:"http"===e||"https"===e?{value:e,valid:t}:{value:0,valid:t}:{valid:!1,message:"Must be a string or a boolean."}},default:!1}),t("link_context_toolbar",{processor:"boolean",default:!1}),t("link_list",{processor:e=>o(e)||c(e)||u(e,r)}),t("link_default_target",{processor:"string"}),t("link_default_protocol",{processor:"string",default:"https"}),t("link_target_list",{processor:e=>a(e)||u(e,r),default:!0}),t("link_rel_list",{processor:"object[]",default:[]}),t("link_class_list",{processor:"object[]",default:[]}),t("link_title",{processor:"boolean",default:!0}),t("allow_unsafe_link_target",{processor:"boolean",default:!1}),t("link_quicklink",{processor:"boolean",default:!1})})(e),(e=>{e.addCommand("mceLink",((t,n)=>{!0!==(null==n?void 0:n.dialog)&&R(e)?e.dispatch("contexttoolbar-show",{toolbarKey:"quicklink"}):Oe(e)}))})(e),De(e),(e=>{e.addShortcut("Meta+K","",(()=>{e.execCommand("mceLink")}))})(e)}))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/lists/plugin.min.js b/apps/web-antd/public/tinymce/plugins/lists/plugin.min.js new file mode 100644 index 0000000..8231445 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/lists/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>t=>(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(n=o=e,(r=String).prototype.isPrototypeOf(n)||(null===(s=o.constructor)||void 0===s?void 0:s.name)===r.name)?"string":t;var n,o,r,s})(t)===e,n=e=>t=>typeof t===e,o=t("string"),r=t("object"),s=t("array"),i=n("boolean"),l=e=>!(e=>null==e)(e),a=n("function"),d=n("number"),c=()=>{},m=e=>()=>e,u=(e,t)=>e===t,p=e=>t=>!e(t),g=m(!1);class h{constructor(e,t){this.tag=e,this.value=t}static some(e){return new h(!0,e)}static none(){return h.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?h.some(e(this.value)):h.none()}bind(e){return this.tag?e(this.value):h.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:h.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return l(e)?h.some(e):h.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}h.singletonNone=new h(!1);const f=Array.prototype.slice,y=Array.prototype.indexOf,v=Array.prototype.push,C=(e,t)=>{return n=e,o=t,y.call(n,o)>-1;var n,o},b=(e,t)=>{for(let n=0,o=e.length;n{const n=e.length,o=new Array(n);for(let r=0;r{for(let n=0,o=e.length;n{const n=[];for(let o=0,r=e.length;o(S(e,((e,o)=>{n=t(n,e,o)})),n),A=(e,t,n)=>{for(let o=0,r=e.length;oA(e,t,g),w=(e,t)=>(e=>{const t=[];for(let n=0,o=e.length;n{const t=f.call(e,0);return t.reverse(),t},E=(e,t)=>t>=0&&tE(e,0),D=e=>E(e,e.length-1),B=(e,t)=>{const n=[],o=a(t)?e=>b(n,(n=>t(n,e))):e=>C(n,e);for(let t=0,r=e.length;te.exists((e=>n(e,t))),P=(e,t,n)=>e.isSome()&&t.isSome()?h.some(n(e.getOrDie(),t.getOrDie())):h.none(),I=e=>{if(null==e)throw new Error("Node cannot be null or undefined");return{dom:e}},R=(e,t)=>{const n=(t||document).createElement("div");if(n.innerHTML=e,!n.hasChildNodes()||n.childNodes.length>1){const t="HTML does not have a single root node";throw console.error(t,e),new Error(t)}return I(n.childNodes[0])},U=(e,t)=>{const n=(t||document).createElement(e);return I(n)},$=I,_=(e,t)=>{const n=e.dom;if(1!==n.nodeType)return!1;{const e=n;if(void 0!==e.matches)return e.matches(t);if(void 0!==e.msMatchesSelector)return e.msMatchesSelector(t);if(void 0!==e.webkitMatchesSelector)return e.webkitMatchesSelector(t);if(void 0!==e.mozMatchesSelector)return e.mozMatchesSelector(t);throw new Error("Browser lacks native selectors")}},H=(e,t)=>e.dom===t.dom,F=_,V="undefined"!=typeof window?window:Function("return this;")(),j=(e,t)=>((e,t)=>{let n=null!=t?t:V;for(let t=0;t{const t=j("ownerDocument.defaultView",e);return r(e)&&((e=>((e,t)=>{const n=((e,t)=>j(e,t))(e,t);if(null==n)throw new Error(e+" not available on this browser");return n})("HTMLElement",e))(t).prototype.isPrototypeOf(e)||/^HTML\w*Element$/.test(K(e).constructor.name))},Q=e=>e.dom.nodeName.toLowerCase(),W=e=>e.dom.nodeType,q=e=>t=>W(t)===e,Z=e=>G(e)&&z(e.dom),G=q(1),J=q(3),X=q(9),Y=q(11),ee=e=>t=>G(t)&&Q(t)===e,te=e=>h.from(e.dom.parentNode).map($),ne=e=>N(e.dom.childNodes,$),oe=(e,t)=>{const n=e.dom.childNodes;return h.from(n[t]).map($)},re=e=>oe(e,0),se=e=>oe(e,e.dom.childNodes.length-1),ie=a(Element.prototype.attachShadow)&&a(Node.prototype.getRootNode)?e=>$(e.dom.getRootNode()):e=>X(e)?e:$(e.dom.ownerDocument),le=e=>$(e.dom.host),ae=e=>{const t=J(e)?e.dom.parentNode:e.dom;if(null==t||null===t.ownerDocument)return!1;const n=t.ownerDocument;return(e=>{const t=ie(e);return Y(n=t)&&l(n.dom.host)?h.some(t):h.none();var n})($(t)).fold((()=>n.body.contains(t)),(o=ae,r=le,e=>o(r(e))));var o,r};var de=(e,t,n,o,r)=>e(n,o)?h.some(n):a(r)&&r(n)?h.none():t(n,o,r);const ce=(e,t,n)=>{let o=e.dom;const r=a(n)?n:g;for(;o.parentNode;){o=o.parentNode;const e=$(o);if(t(e))return h.some(e);if(r(e))break}return h.none()},me=(e,t,n)=>de(((e,t)=>t(e)),ce,e,t,n),ue=(e,t,n)=>ce(e,(e=>_(e,t)),n),pe=(e,t)=>{te(e).each((n=>{n.dom.insertBefore(t.dom,e.dom)}))},ge=(e,t)=>{e.dom.appendChild(t.dom)},he=(e,t)=>{S(t,(t=>{ge(e,t)}))},fe=e=>{e.dom.textContent="",S(ne(e),(e=>{ye(e)}))},ye=e=>{const t=e.dom;null!==t.parentNode&&t.parentNode.removeChild(t)};var ve=tinymce.util.Tools.resolve("tinymce.dom.RangeUtils"),Ce=tinymce.util.Tools.resolve("tinymce.dom.TreeWalker"),be=tinymce.util.Tools.resolve("tinymce.util.VK");const Ne=e=>N(e,$),Se=Object.keys,Le=(e,t)=>{const n=Se(e);for(let o=0,r=n.length;o{const n=e.dom;Le(t,((e,t)=>{((e,t,n)=>{if(!(o(n)||i(n)||d(n)))throw console.error("Invalid call to Attribute.set. Key ",t,":: Value ",n,":: Element ",e),new Error("Attribute value was not simple");e.setAttribute(t,n+"")})(n,t,e)}))},Ae=e=>O(e.dom.attributes,((e,t)=>(e[t.name]=t.value,e)),{}),Te=e=>((e,t)=>$(e.dom.cloneNode(!0)))(e),we=(e,t)=>{const n=((e,t)=>{const n=U(t),o=Ae(e);return Oe(n,o),n})(e,t);var o,r;r=n,(e=>h.from(e.dom.nextSibling).map($))(o=e).fold((()=>{te(o).each((e=>{ge(e,r)}))}),(e=>{pe(e,r)}));const s=ne(e);return he(n,s),ye(e),n};var xe=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),Ee=tinymce.util.Tools.resolve("tinymce.util.Tools");const ke=e=>t=>l(t)&&t.nodeName.toLowerCase()===e,De=e=>t=>l(t)&&e.test(t.nodeName),Be=e=>l(e)&&3===e.nodeType,Me=e=>l(e)&&1===e.nodeType,Pe=De(/^(OL|UL|DL)$/),Ie=De(/^(OL|UL)$/),Re=ke("ol"),Ue=De(/^(LI|DT|DD)$/),$e=De(/^(DT|DD)$/),_e=De(/^(TH|TD)$/),He=ke("br"),Fe=(e,t)=>l(t)&&t.nodeName in e.schema.getTextBlockElements(),Ve=(e,t)=>l(e)&&e.nodeName in t,je=(e,t)=>l(t)&&t.nodeName in e.schema.getVoidElements(),Ke=(e,t,n)=>{const o=e.isEmpty(t);return!(n&&e.select("span[data-mce-type=bookmark]",t).length>0)&&o},ze=(e,t)=>e.isChildOf(t,e.getRoot()),Qe=e=>t=>t.options.get(e),We=Qe("lists_indent_on_tab"),qe=Qe("forced_root_block"),Ze=Qe("forced_root_block_attrs"),Ge=(e,t,n={})=>{const o=e.dom,r=e.schema.getBlockElements(),s=o.createFragment(),i=qe(e),l=Ze(e);let a,d,c=!1;for(d=o.create(i,{...l,...n.style?{style:n.style}:{}}),Ve(t.firstChild,r)||s.appendChild(d);a=t.firstChild;){const e=a.nodeName;c||"SPAN"===e&&"bookmark"===a.getAttribute("data-mce-type")||(c=!0),Ve(a,r)?(s.appendChild(a),d=null):(d||(d=o.create(i,l),s.appendChild(d)),d.appendChild(a))}return!c&&d&&d.appendChild(o.create("br",{"data-mce-bogus":"1"})),s},Je=xe.DOM,Xe=ee("dd"),Ye=ee("dt"),et=(e,t)=>{var n;Xe(t)?we(t,"dt"):Ye(t)&&(n=t,h.from(n.dom.parentElement).map($)).each((n=>((e,t,n)=>{const o=Je.select('span[data-mce-type="bookmark"]',t),r=Ge(e,n),s=Je.createRng();s.setStartAfter(n),s.setEndAfter(t);const i=s.extractContents();for(let t=i.firstChild;t;t=t.firstChild)if("LI"===t.nodeName&&e.dom.isEmpty(t)){Je.remove(t);break}e.dom.isEmpty(i)||Je.insertAfter(i,t),Je.insertAfter(r,t);const l=n.parentElement;l&&Ke(e.dom,l)&&(e=>{const t=e.parentNode;t&&Ee.each(o,(e=>{t.insertBefore(e,n.parentNode)})),Je.remove(e)})(l),Je.remove(n),Ke(e.dom,t)&&Je.remove(t)})(e,n.dom,t.dom)))},tt=e=>{Ye(e)&&we(e,"dd")},nt=(e,t)=>{if(Be(e))return{container:e,offset:t};const n=ve.getNode(e,t);return Be(n)?{container:n,offset:t>=e.childNodes.length?n.data.length:0}:n.previousSibling&&Be(n.previousSibling)?{container:n.previousSibling,offset:n.previousSibling.data.length}:n.nextSibling&&Be(n.nextSibling)?{container:n.nextSibling,offset:0}:{container:e,offset:t}},ot=e=>{const t=e.cloneRange(),n=nt(e.startContainer,e.startOffset);t.setStart(n.container,n.offset);const o=nt(e.endContainer,e.endOffset);return t.setEnd(o.container,o.offset),t},rt=["OL","UL","DL"],st=rt.join(","),it=(e,t)=>{const n=t||e.selection.getStart(!0);return e.dom.getParent(n,st,dt(e,n))},lt=e=>{const t=e.selection.getSelectedBlocks();return L(((e,t)=>{const n=Ee.map(t,(t=>e.dom.getParent(t,"li,dd,dt",dt(e,t))||t));return B(n)})(e,t),Ue)},at=(e,t)=>{const n=e.dom.getParents(t,"TD,TH");return n.length>0?n[0]:e.getBody()},dt=(e,t)=>{const n=e.dom.getParents(t,e.dom.isBlock),o=T(n,(t=>{return(t=>t.nodeName.toLowerCase()!==qe(e))(t)&&(n=e.schema,!Pe(o=t)&&!Ue(o)&&b(rt,(e=>n.isValidChild(o.nodeName,e))));var n,o}));return o.getOr(e.getBody())},ct=(e,t)=>{const n=e.dom.getParents(t,"ol,ul",dt(e,t));return D(n)},mt=(e,t)=>{const n=N(t,(t=>ct(e,t).getOr(t)));return B(n)},ut=e=>/\btox\-/.test(e.className),pt=(e,t)=>A(e,Pe,_e).exists((e=>e.nodeName===t&&!ut(e))),gt=(e,t)=>null!==t&&!e.dom.isEditable(t),ht=(e,t)=>{const n=e.dom.getParent(t,"ol,ul,dl");return gt(e,n)},ft=(e,t)=>{const n=e.selection.getNode();return t({parents:e.dom.getParents(n),element:n}),e.on("NodeChange",t),()=>e.off("NodeChange",t)},yt=(e,t)=>{const n=(t||document).createDocumentFragment();return S(e,(e=>{n.appendChild(e.dom)})),$(n)},vt=(e,t,n)=>e.dispatch("ListMutation",{action:t,element:n}),Ct=(bt=/^\s+|\s+$/g,e=>e.replace(bt,""));var bt;const Nt=(e,t,n)=>{((e,t,n)=>{if(!o(n))throw console.error("Invalid call to CSS.set. Property ",t,":: Value ",n,":: Element ",e),new Error("CSS value must be a string: "+n);(e=>void 0!==e.style&&a(e.style.getPropertyValue))(e)&&e.style.setProperty(t,n)})(e.dom,t,n)},St=e=>F(e,"OL,UL"),Lt=e=>re(e).exists(St),Ot=e=>"listAttributes"in e,At=e=>"isComment"in e,Tt=e=>e.depth>0,wt=e=>e.isSelected,xt=e=>{const t=ne(e),n=se(e).exists(St)?t.slice(0,-1):t;return N(n,Te)},Et=(e,t)=>{ge(e.item,t.list)},kt=(e,t)=>{const n={list:U(t,e),item:U("li",e)};return ge(n.list,n.item),n},Dt=(e,t,n)=>{const o=t.slice(0,n.depth);return D(o).each((t=>{if(Ot(n)){const o=((e,t,n)=>{const o=U("li",e);return Oe(o,t),he(o,n),o})(e,n.itemAttributes,n.content);((e,t)=>{ge(e.list,t),e.item=t})(t,o),((e,t)=>{Q(e.list)!==t.listType&&(e.list=we(e.list,t.listType)),Oe(e.list,t.listAttributes)})(t,n)}else if((e=>"isFragment"in e)(n))he(t.item,n.content);else{const e=R(`\x3c!--${n.content}--\x3e`);ge(t.list,e)}})),o},Bt=(e,t)=>{let n=h.none();const o=O(t,((t,o,r)=>At(o)?0===r?(n=h.some(o),t):Dt(e,t,o):o.depth>t.length?((e,t,n)=>{const o=((e,t,n)=>{const o=[];for(let r=0;r{for(let t=1;t{for(let t=0;t{Ot(t)&&(Oe(e.list,t.listAttributes),Oe(e.item,t.itemAttributes)),he(e.item,t.content)}))})(o,n),r=o,P(D(t),k(r),Et),t.concat(o)})(e,t,o):Dt(e,t,o)),[]);return n.each((e=>{const t=R(`\x3c!--${e.content}--\x3e`);k(o).each((e=>{((e,t)=>{re(e).fold((()=>{ge(e,t)}),(n=>{e.dom.insertBefore(t.dom,n.dom)}))})(e.list,t)}))})),k(o).map((e=>e.list))},Mt=e=>(S(e,((t,n)=>{((e,t)=>{const n=e[t].depth,o=e=>e.depth===n&&!e.dirty,r=e=>e.depthA(e.slice(t+1),o,r)))})(e,n).fold((()=>{t.dirty&&Ot(t)&&(e=>{e.listAttributes=((e,t)=>{const n={};var o;return((e,t,n,o)=>{Le(e,((e,r)=>{(t(e,r)?n:o)(e,r)}))})(e,t,(o=n,(e,t)=>{o[t]=e}),c),n})(e.listAttributes,((e,t)=>"start"!==t))})(t)}),(e=>{return o=e,void(Ot(n=t)&&Ot(o)&&(n.listType=o.listType,n.listAttributes={...o.listAttributes}));var n,o}))})),e),Pt=(e,t,n,o)=>{var r,s;if(8===W(s=o)||"#comment"===Q(s))return[{depth:e+1,content:null!==(r=o.dom.nodeValue)&&void 0!==r?r:"",dirty:!1,isSelected:!1,isComment:!0}];t.each((e=>{H(e.start,o)&&n.set(!0)}));const i=((e,t,n)=>te(e).filter(G).map((o=>({depth:t,dirty:!1,isSelected:n,content:xt(e),itemAttributes:Ae(e),listAttributes:Ae(o),listType:Q(o),isInPreviousLi:!1}))))(o,e,n.get());t.each((e=>{H(e.end,o)&&n.set(!1)}));const l=se(o).filter(St).map((o=>Rt(e,t,n,o))).getOr([]);return i.toArray().concat(l)},It=(e,t,n,o)=>re(o).filter(St).fold((()=>Pt(e,t,n,o)),(r=>{const s=O(ne(o),((o,s,i)=>{if(0===i)return o;if(F(s,"LI"))return o.concat(Pt(e,t,n,s));{const t={isFragment:!0,depth:e,content:[s],isSelected:!1,dirty:!1,parentListType:Q(r)};return o.concat(t)}}),[]);return Rt(e,t,n,r).concat(s)})),Rt=(e,t,n,o)=>w(ne(o),(o=>(St(o)?Rt:It)(e+1,t,n,o))),Ut=(e,t,n)=>{const o=((e,t)=>{const n=(e=>{let t=!1;return{get:()=>t,set:e=>{t=e}}})();return N(e,(e=>({sourceList:e,entries:Rt(0,t,n,e)})))})(t,(e=>{const t=N(lt(e),$);return P(T(t,p(Lt)),T(x(t),p(Lt)),((e,t)=>({start:e,end:t})))})(e));S(o,(t=>{((e,t)=>{S(L(e,wt),(e=>((e,t)=>{switch(e){case"Indent":t.depth++;break;case"Outdent":t.depth--;break;case"Flatten":t.depth=0}t.dirty=!0})(t,e)))})(t.entries,n);const o=((e,t)=>w(((e,t)=>{if(0===e.length)return[];{let n=t(e[0]);const o=[];let r=[];for(let s=0,i=e.length;sk(t).exists(Tt)?((e,t)=>{const n=Mt(t);return Bt(e.contentDocument,n).toArray()})(e,t):((e,t)=>{const n=Mt(t);return N(n,(t=>{const n=At(t)?yt([R(`\x3c!--${t.content}--\x3e`)]):yt(t.content),o=Ot(t)?t.itemAttributes:{};return $(Ge(e,n.dom,o))}))})(e,t))))(e,t.entries);var r;S(o,(t=>{vt(e,"Indent"===n?"IndentList":"OutdentList",t.dom)})),r=t.sourceList,S(o,(e=>{pe(r,e)})),ye(t.sourceList)}))},$t=(e,t)=>{const n=Ne((e=>{const t=(e=>{const t=ct(e,e.selection.getStart()),n=L(e.selection.getSelectedBlocks(),Ie);return t.toArray().concat(n)})(e),n=(e=>{const t=e.selection.getStart();return e.dom.getParents(t,"ol,ul",dt(e,t))})(e);return T(n,(e=>{return t=$(e),te(t).exists((e=>Ue(e.dom)&&re(e).exists((e=>!Pe(e.dom)))&&se(e).exists((e=>!Pe(e.dom)))));var t})).fold((()=>mt(e,t)),(e=>[e]))})(e)),o=Ne((e=>L(lt(e),$e))(e));let r=!1;if(n.length||o.length){const s=e.selection.getBookmark();Ut(e,n,t),((e,t,n)=>{S(n,"Indent"===t?tt:t=>et(e,t))})(e,t,o),e.selection.moveToBookmark(s),e.selection.setRng(ot(e.selection.getRng())),e.nodeChanged(),r=!0}return r},_t=(e,t)=>!(e=>{const t=it(e);return gt(e,t)})(e)&&$t(e,t),Ht=e=>_t(e,"Indent"),Ft=e=>_t(e,"Outdent"),Vt=e=>_t(e,"Flatten"),jt=e=>"\ufeff"===e;var Kt=tinymce.util.Tools.resolve("tinymce.dom.BookmarkManager");const zt=xe.DOM,Qt=e=>{const t={},n=n=>{let o=e[n?"startContainer":"endContainer"],r=e[n?"startOffset":"endOffset"];if(Me(o)){const e=zt.create("span",{"data-mce-type":"bookmark"});o.hasChildNodes()?(r=Math.min(r,o.childNodes.length-1),n?o.insertBefore(e,o.childNodes[r]):zt.insertAfter(e,o.childNodes[r])):o.appendChild(e),o=e,r=0}t[n?"startContainer":"endContainer"]=o,t[n?"startOffset":"endOffset"]=r};return n(!0),e.collapsed||n(),t},Wt=e=>{const t=t=>{let n=e[t?"startContainer":"endContainer"],o=e[t?"startOffset":"endOffset"];if(n){if(Me(n)&&n.parentNode){const e=n;o=(e=>{var t;let n=null===(t=e.parentNode)||void 0===t?void 0:t.firstChild,o=0;for(;n;){if(n===e)return o;Me(n)&&"bookmark"===n.getAttribute("data-mce-type")||o++,n=n.nextSibling}return-1})(n),n=n.parentNode,zt.remove(e),!n.hasChildNodes()&&zt.isBlock(n)&&n.appendChild(zt.create("br"))}e[t?"startContainer":"endContainer"]=n,e[t?"startOffset":"endOffset"]=o}};t(!0),t();const n=zt.createRng();return n.setStart(e.startContainer,e.startOffset),e.endContainer&&n.setEnd(e.endContainer,e.endOffset),ot(n)},qt=e=>{switch(e){case"UL":return"ToggleUlList";case"OL":return"ToggleOlList";case"DL":return"ToggleDLList"}},Zt=(e,t)=>{Ee.each(t,((t,n)=>{e.setAttribute(n,t)}))},Gt=(e,t,n)=>{((e,t,n)=>{const o=n["list-style-type"]?n["list-style-type"]:null;e.setStyle(t,"list-style-type",o)})(e,t,n),((e,t,n)=>{Zt(t,n["list-attributes"]),Ee.each(e.select("li",t),(e=>{Zt(e,n["list-item-attributes"])}))})(e,t,n)},Jt=(e,t)=>l(t)&&!Ve(t,e.schema.getBlockElements()),Xt=(e,t,n,o)=>{let r=t[n?"startContainer":"endContainer"];const s=t[n?"startOffset":"endOffset"];Me(r)&&(r=r.childNodes[Math.min(s,r.childNodes.length-1)]||r),!n&&He(r.nextSibling)&&(r=r.nextSibling);const i=(t,n)=>{var r;const s=new Ce(t,(t=>{for(;!e.dom.isBlock(t)&&t.parentNode&&o!==t;)t=t.parentNode;return t})(t)),i=n?"next":"prev";let l;for(;l=s[i]();)if(!je(e,l)&&!jt(l.textContent)&&0!==(null===(r=l.textContent)||void 0===r?void 0:r.length))return h.some(l);return h.none()};if(n&&Be(r))if(jt(r.textContent))r=i(r,!1).getOr(r);else for(null!==r.parentNode&&Jt(e,r.parentNode)&&(r=r.parentNode);null!==r.previousSibling&&(Jt(e,r.previousSibling)||Be(r.previousSibling));)r=r.previousSibling;if(!n&&Be(r))if(jt(r.textContent))r=i(r,!0).getOr(r);else for(null!==r.parentNode&&Jt(e,r.parentNode)&&(r=r.parentNode);null!==r.nextSibling&&(Jt(e,r.nextSibling)||Be(r.nextSibling));)r=r.nextSibling;for(;r.parentNode!==o;){const t=r.parentNode;if(Fe(e,r))return r;if(/^(TD|TH)$/.test(t.nodeName))return r;r=t}return r},Yt=(e,t,n)=>{const o=e.selection.getRng();let r="LI";const s=dt(e,((e,t)=>{const n=e.selection.getStart(!0),o=Xt(e,t,!0,e.getBody());return r=$(o),s=$(t.commonAncestorContainer),i=r,l=function(e,...t){return(...n)=>{const o=t.concat(n);return e.apply(null,o)}}(H,s),ce(i,l,void 0).isSome()?t.commonAncestorContainer:n;var r,s,i,l})(e,o)),i=e.dom;if("false"===i.getContentEditable(e.selection.getNode()))return;"DL"===(t=t.toUpperCase())&&(r="DT");const l=Qt(o),a=L(((e,t,n)=>{const o=[],r=e.dom,s=Xt(e,t,!0,n),i=Xt(e,t,!1,n);let l;const a=[];for(let e=s;e&&(a.push(e),e!==i);e=e.nextSibling);return Ee.each(a,(t=>{var s;if(Fe(e,t))return o.push(t),void(l=null);if(r.isBlock(t)||He(t))return He(t)&&r.remove(t),void(l=null);const i=t.nextSibling;Kt.isBookmarkNode(t)&&(Pe(i)||Fe(e,i)||!i&&t.parentNode===n)?l=null:(l||(l=r.create("p"),null===(s=t.parentNode)||void 0===s||s.insertBefore(l,t),o.push(l)),l.appendChild(t))})),o})(e,o,s),e.dom.isEditable);Ee.each(a,(o=>{let s;const l=o.previousSibling,a=o.parentNode;Ue(a)||(l&&Pe(l)&&l.nodeName===t&&((e,t,n)=>{const o=e.getStyle(t,"list-style-type");let r=n?n["list-style-type"]:"";return r=null===r?"":r,o===r})(i,l,n)?(s=l,o=i.rename(o,r),l.appendChild(o)):(s=i.create(t),a.insertBefore(s,o),s.appendChild(o),o=i.rename(o,r)),((e,t,n)=>{Ee.each(["margin","margin-right","margin-bottom","margin-left","margin-top","padding","padding-right","padding-bottom","padding-left","padding-top"],(n=>e.setStyle(t,n,"")))})(i,o),Gt(i,s,n),tn(e.dom,s))})),e.selection.setRng(Wt(l))},en=(e,t,n)=>{return((e,t)=>Pe(e)&&e.nodeName===(null==t?void 0:t.nodeName))(t,n)&&((e,t,n)=>e.getStyle(t,"list-style-type",!0)===e.getStyle(n,"list-style-type",!0))(e,t,n)&&(o=n,t.className===o.className);var o},tn=(e,t)=>{let n,o=t.nextSibling;if(en(e,t,o)){const r=o;for(;n=r.firstChild;)t.appendChild(n);e.remove(r)}if(o=t.previousSibling,en(e,t,o)){const r=o;for(;n=r.lastChild;)t.insertBefore(n,t.firstChild);e.remove(r)}},nn=(e,t,n,o)=>{if(t.nodeName!==n){const r=e.dom.rename(t,n);Gt(e.dom,r,o),vt(e,qt(n),r)}else Gt(e.dom,t,o),vt(e,qt(n),t)},on=(e,t,n,o)=>{if(t.classList.forEach(((e,n,o)=>{e.startsWith("tox-")&&(o.remove(e),0===o.length&&t.removeAttribute("class"))})),t.nodeName!==n){const r=e.dom.rename(t,n);Gt(e.dom,r,o),vt(e,qt(n),r)}else Gt(e.dom,t,o),vt(e,qt(n),t)},rn=e=>"list-style-type"in e,sn=(e,t,n)=>{const o=it(e);if(ht(e,o))return;const s=(e=>{const t=it(e),n=e.selection.getSelectedBlocks();return((e,t)=>l(e)&&1===t.length&&t[0]===e)(t,n)?(e=>L(e.querySelectorAll(st),Pe))(t):L(n,(e=>Pe(e)&&t!==e))})(e),i=r(n)?n:{};s.length>0?((e,t,n,o,r)=>{const s=Pe(t);if(!s||t.nodeName!==o||rn(r)||ut(t)){Yt(e,o,r);const i=Qt(e.selection.getRng()),l=s?[t,...n]:n,a=s&&ut(t)?on:nn;Ee.each(l,(t=>{a(e,t,o,r)})),e.selection.setRng(Wt(i))}else Vt(e)})(e,o,s,t,i):((e,t,n,o)=>{if(t!==e.getBody())if(t)if(t.nodeName!==n||rn(o)||ut(t)){const r=Qt(e.selection.getRng());ut(t)&&t.classList.forEach(((e,n,o)=>{e.startsWith("tox-")&&(o.remove(e),0===o.length&&t.removeAttribute("class"))})),Gt(e.dom,t,o);const s=e.dom.rename(t,n);tn(e.dom,s),e.selection.setRng(Wt(r)),Yt(e,n,o),vt(e,qt(n),s)}else Vt(e);else Yt(e,n,o),vt(e,qt(n),t)})(e,o,t,i)},ln=xe.DOM,an=(e,t)=>{const n=Ee.grep(e.select("ol,ul",t));Ee.each(n,(t=>{((e,t)=>{const n=t.parentElement;if(n&&"LI"===n.nodeName&&n.firstChild===t){const o=n.previousSibling;o&&"LI"===o.nodeName?(o.appendChild(t),Ke(e,n)&&ln.remove(n)):ln.setStyle(n,"listStyleType","none")}if(Pe(n)){const e=n.previousSibling;e&&"LI"===e.nodeName&&e.appendChild(t)}})(e,t)}))},dn=(e,t,n,o)=>{let r=t.startContainer;const s=t.startOffset;if(Be(r)&&(n?s0))return r;const i=e.schema.getNonEmptyElements();Me(r)&&(r=ve.getNode(r,s));const l=new Ce(r,o);n&&((e,t)=>!!He(t)&&e.isBlock(t.nextSibling)&&!He(t.previousSibling))(e.dom,r)&&l.next();const a=n?l.next.bind(l):l.prev2.bind(l);for(;r=a();){if("LI"===r.nodeName&&!r.hasChildNodes())return r;if(i[r.nodeName])return r;if(Be(r)&&r.data.length>0)return r}return null},cn=(e,t)=>{const n=t.childNodes;return 1===n.length&&!Pe(n[0])&&e.isBlock(n[0])},mn=e=>h.from(e).map($).filter(Z).exists((e=>((e,t=!1)=>{return ae(e)?e.dom.isContentEditable:(n=e,de(((e,t)=>_(e,t)),ue,n,"[contenteditable]",void 0)).fold(m(t),(e=>"true"===(e=>e.dom.contentEditable)(e)));var n})(e)&&!C(["details"],Q(e)))),un=(e,t,n)=>{let o;const r=cn(e,n)?n.firstChild:n;if(((e,t)=>{cn(e,t)&&mn(t.firstChild)&&e.remove(t.firstChild,!0)})(e,t),!Ke(e,t,!0))for(;o=t.firstChild;)r.appendChild(o)},pn=(e,t,n)=>{let o;const r=t.parentNode;if(!ze(e,t)||!ze(e,n))return;Pe(n.lastChild)&&(o=n.lastChild),r===n.lastChild&&He(r.previousSibling)&&e.remove(r.previousSibling);const s=n.lastChild;s&&He(s)&&t.hasChildNodes()&&e.remove(s),Ke(e,n,!0)&&fe($(n)),un(e,t,n),o&&n.appendChild(o);const i=((e,t)=>{const n=e.dom,o=t.dom;return n!==o&&n.contains(o)})($(n),$(t))?e.getParents(t,Pe,n):[];e.remove(t),S(i,(t=>{Ke(e,t)&&t!==e.getRoot()&&e.remove(t)}))},gn=(e,t)=>{const n=e.dom,o=e.selection,r=o.getStart(),s=at(e,r),i=n.getParent(o.getStart(),"LI",s);if(i){const r=i.parentElement;if(r===e.getBody()&&Ke(n,r))return!0;const l=ot(o.getRng()),a=n.getParent(dn(e,l,t,s),"LI",s),d=a&&(t?n.isChildOf(i,a):n.isChildOf(a,i));if(a&&a!==i&&!d)return e.undoManager.transact((()=>{var n,o;t?((e,t,n,o)=>{const r=e.dom;if(r.isEmpty(o))((e,t,n)=>{fe($(n)),pn(e.dom,t,n),e.selection.setCursorLocation(n,0)})(e,n,o);else{const s=Qt(t);pn(r,n,o),e.selection.setRng(Wt(s))}})(e,l,a,i):(null===(o=(n=i).parentNode)||void 0===o?void 0:o.firstChild)===n?Ft(e):((e,t,n,o)=>{const r=Qt(t);pn(e.dom,n,o);const s=Wt(r);e.selection.setRng(s)})(e,l,i,a)})),!0;if(d&&!t&&a!==i)return e.undoManager.transact((()=>{if(l.commonAncestorContainer.parentElement){const t=Qt(l),o=l.commonAncestorContainer.parentElement;un(n,l.commonAncestorContainer.parentElement,a),o.remove();const r=Wt(t);e.selection.setRng(r)}})),!0;if(!a&&!t&&0===l.startOffset&&0===l.endOffset)return e.undoManager.transact((()=>{Vt(e)})),!0}return!1},hn=e=>{const t=e.selection.getStart(),n=at(e,t);return e.dom.getParent(t,"LI,DT,DD",n)||lt(e).length>0},fn=(e,t)=>{const n=e.selection;return!ht(e,n.getNode())&&(n.isCollapsed()?((e,t)=>gn(e,t)||((e,t)=>{const n=e.dom,o=e.selection.getStart(),r=at(e,o),s=n.getParent(o,n.isBlock,r);if(s&&n.isEmpty(s,void 0,{checkRootAsContent:!0})){const o=ot(e.selection.getRng()),i=n.getParent(dn(e,o,t,r),"LI",r);if(i){const l=e=>C(["td","th","caption"],Q(e)),a=e=>e.dom===r;return!!((e,t,n=u)=>P(e,t,n).getOr(e.isNone()&&t.isNone()))(me($(i),l,a),me($(o.startContainer),l,a),H)&&(e.undoManager.transact((()=>{const o=i.parentNode;((e,t,n)=>{const o=e.getParent(t.parentNode,e.isBlock,n);e.remove(t),o&&e.isEmpty(o)&&e.remove(o)})(n,s,r),tn(n,o),e.selection.select(i,!0),e.selection.collapse(t)})),!0)}}return!1})(e,t))(e,t):(e=>!!hn(e)&&(e.undoManager.transact((()=>{e.execCommand("Delete"),an(e.dom,e.getBody())})),!0))(e))},yn=e=>{const t=x(Ct(e).split("")),n=N(t,((e,t)=>{const n=e.toUpperCase().charCodeAt(0)-"A".charCodeAt(0)+1;return Math.pow(26,t)*n}));return O(n,((e,t)=>e+t),0)},vn=e=>{if(--e<0)return"";{const t=e%26,n=Math.floor(e/26);return vn(n)+String.fromCharCode("A".charCodeAt(0)+t)}},Cn=e=>{const t=parseInt(e.start,10);return M(e.listStyleType,"upper-alpha")?vn(t):M(e.listStyleType,"lower-alpha")?vn(t).toLowerCase():e.start},bn=(e,t)=>()=>{const n=it(e);return l(n)&&n.nodeName===t},Nn=e=>{e.addCommand("mceListProps",(()=>{(e=>{const t=it(e);Re(t)&&!ht(e,t)&&e.windowManager.open({title:"List Properties",body:{type:"panel",items:[{type:"input",name:"start",label:"Start list at number",inputMode:"numeric"}]},initialData:{start:Cn({start:e.dom.getAttrib(t,"start","1"),listStyleType:h.from(e.dom.getStyle(t,"list-style-type"))})},buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],onSubmit:t=>{(e=>{switch((e=>/^[0-9]+$/.test(e)?2:/^[A-Z]+$/.test(e)?0:/^[a-z]+$/.test(e)?1:e.length>0?4:3)(e)){case 2:return h.some({listStyleType:h.none(),start:e});case 0:return h.some({listStyleType:h.some("upper-alpha"),start:yn(e).toString()});case 1:return h.some({listStyleType:h.some("lower-alpha"),start:yn(e).toString()});case 3:return h.some({listStyleType:h.none(),start:""});case 4:return h.none()}})(t.getData().start).each((t=>{e.execCommand("mceListUpdate",!1,{attrs:{start:"1"===t.start?"":t.start},styles:{"list-style-type":t.listStyleType.getOr("")}})})),t.close()}})})(e)}))};var Sn=tinymce.util.Tools.resolve("tinymce.html.Node");const Ln=e=>3===e.type,On=e=>0===e.length,An=e=>{const t=(t,n)=>{const o=Sn.create("li");S(t,(e=>o.append(e))),n?e.insert(o,n,!0):e.append(o)},n=O(e.children(),((e,n)=>Ln(n)?[...e,n]:On(e)||Ln(n)?e:(t(e,n),[])),[]);On(n)||t(n)},Tn=(e,t)=>n=>(n.setEnabled(e.selection.isEditable()),ft(e,(o=>{n.setActive(pt(o.parents,t)),n.setEnabled(!ht(e,o.element)&&e.selection.isEditable())}))),wn=(e,t)=>n=>ft(e,(o=>n.setEnabled(pt(o.parents,t)&&!ht(e,o.element))));e.add("lists",(e=>((e=>{(0,e.options.register)("lists_indent_on_tab",{processor:"boolean",default:!0})})(e),(e=>{e.on("PreInit",(()=>{const{parser:t}=e;t.addNodeFilter("ul,ol",(e=>S(e,An)))}))})(e),e.hasPlugin("rtc",!0)?Nn(e):((e=>{We(e)&&(e=>{e.on("keydown",(t=>{t.keyCode!==be.TAB||be.metaKeyPressed(t)||e.undoManager.transact((()=>{(t.shiftKey?Ft(e):Ht(e))&&t.preventDefault()}))}))})(e),(e=>{e.on("ExecCommand",(t=>{const n=t.command.toLowerCase();"delete"!==n&&"forwarddelete"!==n||!hn(e)||an(e.dom,e.getBody())})),e.on("keydown",(t=>{t.keyCode===be.BACKSPACE?fn(e,!1)&&t.preventDefault():t.keyCode===be.DELETE&&fn(e,!0)&&t.preventDefault()}))})(e)})(e),(e=>{e.on("BeforeExecCommand",(t=>{const n=t.command.toLowerCase();"indent"===n?Ht(e):"outdent"===n&&Ft(e)})),e.addCommand("InsertUnorderedList",((t,n)=>{sn(e,"UL",n)})),e.addCommand("InsertOrderedList",((t,n)=>{sn(e,"OL",n)})),e.addCommand("InsertDefinitionList",((t,n)=>{sn(e,"DL",n)})),e.addCommand("RemoveList",(()=>{Vt(e)})),Nn(e),e.addCommand("mceListUpdate",((t,n)=>{r(n)&&((e,t)=>{const n=it(e);null===n||ht(e,n)||e.undoManager.transact((()=>{r(t.styles)&&e.dom.setStyles(n,t.styles),r(t.attrs)&&Le(t.attrs,((t,o)=>e.dom.setAttrib(n,o,t)))}))})(e,n)})),e.addQueryStateHandler("InsertUnorderedList",bn(e,"UL")),e.addQueryStateHandler("InsertOrderedList",bn(e,"OL")),e.addQueryStateHandler("InsertDefinitionList",bn(e,"DL"))})(e)),(e=>{const t=t=>()=>e.execCommand(t);e.hasPlugin("advlist")||(e.ui.registry.addToggleButton("numlist",{icon:"ordered-list",active:!1,tooltip:"Numbered list",onAction:t("InsertOrderedList"),onSetup:Tn(e,"OL")}),e.ui.registry.addToggleButton("bullist",{icon:"unordered-list",active:!1,tooltip:"Bullet list",onAction:t("InsertUnorderedList"),onSetup:Tn(e,"UL")}))})(e),(e=>{const t={text:"List properties...",icon:"ordered-list",onAction:()=>e.execCommand("mceListProps"),onSetup:wn(e,"OL")};e.ui.registry.addMenuItem("listprops",t),e.ui.registry.addContextMenu("lists",{update:t=>{const n=it(e,t);return Re(n)?["listprops"]:[]}})})(e),(e=>({backspaceDelete:t=>{fn(e,t)}}))(e))))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/media/plugin.min.js b/apps/web-antd/public/tinymce/plugins/media/plugin.min.js new file mode 100644 index 0000000..dc10a16 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/media/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>t=>(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(r=o=e,(a=String).prototype.isPrototypeOf(r)||(null===(s=o.constructor)||void 0===s?void 0:s.name)===a.name)?"string":t;var r,o,a,s})(t)===e,r=t("string"),o=t("object"),a=t("array"),s=e=>!(e=>null==e)(e);class i{constructor(e,t){this.tag=e,this.value=t}static some(e){return new i(!0,e)}static none(){return i.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?i.some(e(this.value)):i.none()}bind(e){return this.tag?e(this.value):i.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:i.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return s(e)?i.some(e):i.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}i.singletonNone=new i(!1);const n=Array.prototype.push,l=(e,t)=>{for(let r=0,o=e.length;r{const t=[];for(let r=0,o=e.length;rh(e,t)?i.from(e[t]):i.none(),h=(e,t)=>u.call(e,t),p=e=>t=>t.options.get(e),g=p("audio_template_callback"),b=p("video_template_callback"),w=p("iframe_template_callback"),v=p("media_live_embeds"),f=p("media_filter_html"),y=p("media_url_resolver"),x=p("media_alt_source"),_=p("media_poster"),k=p("media_dimensions");var j=tinymce.util.Tools.resolve("tinymce.util.Tools"),O=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),A=tinymce.util.Tools.resolve("tinymce.html.DomParser");const S=O.DOM,$=e=>e.replace(/px$/,""),C=e=>{const t=e.attr("style"),r=t?S.parseStyle(t):{};return{type:"ephox-embed-iri",source:e.attr("data-ephox-embed-iri"),altsource:"",poster:"",width:d(r,"max-width").map($).getOr(""),height:d(r,"max-height").map($).getOr("")}},T=(e,t)=>{let r={};for(let o=A({validate:!1,forced_root_block:!1},t).parse(e);o;o=o.walk())if(1===o.type){const e=o.name;if(o.attr("data-ephox-embed-iri")){r=C(o);break}r.source||"param"!==e||(r.source=o.attr("movie")),"iframe"!==e&&"object"!==e&&"embed"!==e&&"video"!==e&&"audio"!==e||(r.type||(r.type=e),r=j.extend(o.attributes.map,r)),"source"===e&&(r.source?r.altsource||(r.altsource=o.attr("src")):r.source=o.attr("src")),"img"!==e||r.poster||(r.poster=o.attr("src"))}return r.source=r.source||r.src||"",r.altsource=r.altsource||"",r.poster=r.poster||"",r},z=e=>{var t;const r=null!==(t=e.toLowerCase().split(".").pop())&&void 0!==t?t:"";return d({mp3:"audio/mpeg",m4a:"audio/x-m4a",wav:"audio/wav",mp4:"video/mp4",webm:"video/webm",ogg:"video/ogg",swf:"application/x-shockwave-flash"},r).getOr("")};var D=tinymce.util.Tools.resolve("tinymce.html.Node"),F=tinymce.util.Tools.resolve("tinymce.html.Serializer");const M=(e,t={})=>A({forced_root_block:!1,validate:!1,allow_conditional_comments:!0,...t},e),N=O.DOM,P=e=>/^[0-9.]+$/.test(e)?e+"px":e,R=(e,t)=>{const r=t.attr("style"),o=r?N.parseStyle(r):{};s(e.width)&&(o["max-width"]=P(e.width)),s(e.height)&&(o["max-height"]=P(e.height)),t.attr("style",N.serializeStyle(o))},E=["source","altsource"],U=(e,t,r,o)=>{let a=0,s=0;const i=M(o);i.addNodeFilter("source",(e=>a=e.length));const n=i.parse(e);for(let e=n;e;e=e.walk())if(1===e.type){const o=e.name;if(e.attr("data-ephox-embed-iri")){R(t,e);break}switch(o){case"video":case"object":case"embed":case"img":case"iframe":void 0!==t.height&&void 0!==t.width&&(e.attr("width",t.width),e.attr("height",t.height))}if(r)switch(o){case"video":e.attr("poster",t.poster),e.attr("src",null);for(let r=a;r<2;r++)if(t[E[r]]){const o=new D("source",1);o.attr("src",t[E[r]]),o.attr("type",t[E[r]+"mime"]||null),e.append(o)}break;case"iframe":e.attr("src",t.source);break;case"object":const r=e.getAll("img").length>0;if(t.poster&&!r){e.attr("src",t.poster);const r=new D("img",1);r.attr("src",t.poster),r.attr("width",t.width),r.attr("height",t.height),e.append(r)}break;case"source":if(s<2&&(e.attr("src",t[E[s]]),e.attr("type",t[E[s]+"mime"]||null),!t[E[s]])){e.remove();continue}s++;break;case"img":t.poster||e.remove()}}return F({},o).serialize(n)},L=[{regex:/youtu\.be\/([\w\-_\?&=.]+)/i,type:"iframe",w:560,h:314,url:"www.youtube.com/embed/$1",allowFullscreen:!0},{regex:/youtube\.com(.+)v=([^&]+)(&([a-z0-9&=\-_]+))?/i,type:"iframe",w:560,h:314,url:"www.youtube.com/embed/$2?$4",allowFullscreen:!0},{regex:/youtube.com\/embed\/([a-z0-9\?&=\-_]+)/i,type:"iframe",w:560,h:314,url:"www.youtube.com/embed/$1",allowFullscreen:!0},{regex:/vimeo\.com\/([0-9]+)\?h=(\w+)/,type:"iframe",w:425,h:350,url:"player.vimeo.com/video/$1?h=$2&title=0&byline=0&portrait=0&color=8dc7dc",allowFullscreen:!0},{regex:/vimeo\.com\/(.*)\/([0-9]+)\?h=(\w+)/,type:"iframe",w:425,h:350,url:"player.vimeo.com/video/$2?h=$3&title=0&byline=0",allowFullscreen:!0},{regex:/vimeo\.com\/([0-9]+)/,type:"iframe",w:425,h:350,url:"player.vimeo.com/video/$1?title=0&byline=0&portrait=0&color=8dc7dc",allowFullscreen:!0},{regex:/vimeo\.com\/(.*)\/([0-9]+)/,type:"iframe",w:425,h:350,url:"player.vimeo.com/video/$2?title=0&byline=0",allowFullscreen:!0},{regex:/maps\.google\.([a-z]{2,3})\/maps\/(.+)msid=(.+)/,type:"iframe",w:425,h:350,url:'maps.google.com/maps/ms?msid=$2&output=embed"',allowFullscreen:!1},{regex:/dailymotion\.com\/video\/([^_]+)/,type:"iframe",w:480,h:270,url:"www.dailymotion.com/embed/video/$1",allowFullscreen:!0},{regex:/dai\.ly\/([^_]+)/,type:"iframe",w:480,h:270,url:"www.dailymotion.com/embed/video/$1",allowFullscreen:!0}],I=(e,t)=>{const r=(e=>{const t=e.match(/^(https?:\/\/|www\.)(.+)$/i);return t&&t.length>1?"www."===t[1]?"https://":t[1]:"https://"})(t),o=e.regex.exec(t);let a=r+e.url;if(s(o))for(let e=0;eo[e]?o[e]:""));return a.replace(/\?$/,"")},B=e=>{const t=L.filter((t=>t.regex.test(e)));return t.length>0?j.extend({},t[0],{url:I(t[0],e)}):null},G=(e,t)=>{var r;const o=j.extend({},t);if(!o.source&&(j.extend(o,T(null!==(r=o.embed)&&void 0!==r?r:"",e.schema)),!o.source))return"";o.altsource||(o.altsource=""),o.poster||(o.poster=""),o.source=e.convertURL(o.source,"source"),o.altsource=e.convertURL(o.altsource,"source"),o.sourcemime=z(o.source),o.altsourcemime=z(o.altsource),o.poster=e.convertURL(o.poster,"poster");const a=B(o.source);if(a&&(o.source=a.url,o.type=a.type,o.allowfullscreen=a.allowFullscreen,o.width=o.width||String(a.w),o.height=o.height||String(a.h)),o.embed)return U(o.embed,o,!0,e.schema);{const t=g(e),r=b(e),a=w(e);return o.width=o.width||"300",o.height=o.height||"150",j.each(o,((t,r)=>{o[r]=e.dom.encode(""+t)})),"iframe"===o.type?((e,t)=>{if(t)return t(e);{const t=e.allowfullscreen?' allowFullscreen="1"':"";return'"}})(o,a):"application/x-shockwave-flash"===o.sourcemime?(e=>{let t='';return e.poster&&(t+=''),t+="",t})(o):-1!==o.sourcemime.indexOf("audio")?((e,t)=>t?t(e):'")(o,t):((e,t)=>t?t(e):'")(o,r)}},W=e=>e.hasAttribute("data-mce-object")||e.hasAttribute("data-ephox-embed-iri"),q={},H=e=>t=>G(e,t),J=(e,t)=>{const r=y(e);return r?((e,t,r)=>new Promise(((o,a)=>{const s=r=>(r.html&&(q[e.source]=r),o({url:e.source,html:r.html?r.html:t(e)}));q[e.source]?s(q[e.source]):r({url:e.source}).then(s).catch(a)})))(t,H(e),r):((e,t)=>Promise.resolve({html:t(e),url:e.source}))(t,H(e))},K=(e,t)=>{const r={};return d(e,"dimensions").each((e=>{l(["width","height"],(o=>{d(t,o).orThunk((()=>d(e,o))).each((e=>r[o]=e))}))})),r},Q=(e,t)=>{const r=t&&"dimensions"!==t?((e,t)=>d(t,e).bind((e=>d(e,"meta"))))(t,e).getOr({}):{},a=((e,t,r)=>a=>{const s=()=>d(e,a),n=()=>d(t,a),l=e=>d(e,"value").bind((e=>e.length>0?i.some(e):i.none()));return{[a]:(a===r?s().bind((e=>o(e)?l(e).orThunk(n):n().orThunk((()=>i.from(e))))):n().orThunk((()=>s().bind((e=>o(e)?l(e):i.from(e)))))).getOr("")}})(e,r,t);return{...a("source"),...a("altsource"),...a("poster"),...a("embed"),...K(e,r)}},V=e=>{const t={...e,source:{value:d(e,"source").getOr("")},altsource:{value:d(e,"altsource").getOr("")},poster:{value:d(e,"poster").getOr("")}};return l(["width","height"],(r=>{d(e,r).each((e=>{const o=t.dimensions||{};o[r]=e,t.dimensions=o}))})),t},X=e=>t=>{const r=t&&t.msg?"Media embed handler error: "+t.msg:"Media embed handler threw unknown error.";e.notificationManager.open({type:"error",text:r})},Y=(e,t)=>o=>{if(r(o.url)&&o.url.trim().length>0){const r=o.html,a={...T(r,t.schema),source:o.url,embed:r};e.setData(V(a))}},Z=(e,t)=>{const r=e.dom.select("*[data-mce-object]");e.insertContent(t),((e,t)=>{const r=e.dom.select("*[data-mce-object]");for(let e=0;e=0;o--)t[e]===r[o]&&r.splice(o,1);e.selection.select(r[0])})(e,r),e.nodeChanged()},ee=(e,t)=>s(t)&&"ephox-embed-iri"===t&&s(B(e)),te=(e,t)=>((e,t)=>e.width!==t.width||e.height!==t.height)(e,t)&&ee(t.source,e.type),re=e=>{const t=(e=>{const t=e.selection.getNode(),r=W(t)?e.serializer.serialize(t,{selection:!0}):"",o=T(r,e.schema),a=(()=>{if(ee(o.source,o.type)){const r=e.dom.getRect(t);return{width:r.w.toString().replace(/px$/,""),height:r.h.toString().replace(/px$/,"")}}return{}})();return{embed:r,...o,...a}})(e),r=(e=>{let t=e;return{get:()=>t,set:e=>{t=e}}})(t),o=V(t),a=k(e)?[{type:"sizeinput",name:"dimensions",label:"Constrain proportions",constrain:!0}]:[],s={title:"General",name:"general",items:c([[{name:"source",type:"urlinput",filetype:"media",label:"Source",picker_text:"Browse files"}],a])},i=[];x(e)&&i.push({name:"altsource",type:"urlinput",filetype:"media",label:"Alternative source URL"}),_(e)&&i.push({name:"poster",type:"urlinput",filetype:"image",label:"Media poster (Image URL)"});const n={title:"Advanced",name:"advanced",items:i},l=[s,{title:"Embed",items:[{type:"textarea",name:"embed",label:"Paste your embed code below:"}]}];i.length>0&&l.push(n);const m={type:"tabpanel",tabs:l},u=e.windowManager.open({title:"Insert/Edit Media",size:"normal",body:m,buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],onSubmit:t=>{const o=Q(t.getData());((e,t,r)=>{var o,a;t.embed=te(e,t)&&k(r)?G(r,{...t,embed:""}):U(null!==(o=t.embed)&&void 0!==o?o:"",t,!1,r.schema),t.embed&&(e.source===t.source||(a=t.source,h(q,a)))?Z(r,t.embed):J(r,t).then((e=>{Z(r,e.html)})).catch(X(r))})(r.get(),o,e),t.close()},onChange:(t,o)=>{switch(o.name){case"source":((t,r)=>{const o=Q(r.getData(),"source");t.source!==o.source&&(Y(u,e)({url:o.source,html:""}),J(e,o).then(Y(u,e)).catch(X(e)))})(r.get(),t);break;case"embed":(t=>{var r;const o=Q(t.getData()),a=T(null!==(r=o.embed)&&void 0!==r?r:"",e.schema);t.setData(V(a))})(t);break;case"dimensions":case"altsource":case"poster":((t,r,o)=>{const a=Q(t.getData(),r),s=te(o,a)&&k(e)?{...a,embed:""}:a,i=G(e,s);t.setData(V({...s,embed:i}))})(t,o.name,r.get())}r.set(Q(t.getData()))},initialData:o})};var oe=tinymce.util.Tools.resolve("tinymce.Env");const ae=e=>{const t=e.name;return"iframe"===t||"video"===t||"audio"===t},se=(e,t,r,o=null)=>{const a=e.attr(r);return s(a)?a:h(t,r)?null:o},ie=(e,t,r)=>{const o="img"===t.name||"video"===e.name,a=o?"300":null,s="audio"===e.name?"30":"150",i=o?s:null;t.attr({width:se(e,r,"width",a),height:se(e,r,"height",i)})},ne=(e,t)=>{const r=t.name,o=new D("img",1);return ce(e,t,o),ie(t,o,{}),o.attr({style:t.attr("style"),src:oe.transparentSrc,"data-mce-object":r,class:"mce-object mce-object-"+r}),o},le=(e,t)=>{var r;const o=t.name,a=new D("span",1);a.attr({contentEditable:"false",style:t.attr("style"),"data-mce-object":o,class:"mce-preview-object mce-object-"+o}),ce(e,t,a);const i=e.dom.parseStyle(null!==(r=t.attr("style"))&&void 0!==r?r:""),n=new D(o,1);if(ie(t,n,i),n.attr({src:t.attr("src"),style:t.attr("style"),class:t.attr("class")}),"iframe"===o)n.attr({allowfullscreen:t.attr("allowfullscreen"),frameborder:"0",sandbox:t.attr("sandbox"),referrerpolicy:t.attr("referrerpolicy")});else{l(["controls","crossorigin","currentTime","loop","muted","poster","preload"],(e=>{n.attr(e,t.attr(e))}));const r=a.attr("data-mce-html");s(r)&&((e,t,r,o)=>{const a=M(e.schema).parse(o,{context:t});for(;a.firstChild;)r.append(a.firstChild)})(e,o,n,unescape(r))}const c=new D("span",1);return c.attr("class","mce-shim"),a.append(n),a.append(c),a},ce=(e,t,r)=>{var o;const a=null!==(o=t.attributes)&&void 0!==o?o:[];let s=a.length;for(;s--;){const t=a[s].name;let o=a[s].value;"width"===t||"height"===t||"style"===t||(n="data-mce-",(i=t).length>=9&&i.substr(0,9)===n)||("data"!==t&&"src"!==t||(o=e.convertURL(o,t)),r.attr("data-mce-p-"+t,o))}var i,n;const c=F({inner:!0},e.schema),m=new D("div",1);l(t.children(),(e=>m.append(e)));const u=c.serialize(m);u&&(r.attr("data-mce-html",escape(u)),r.empty())},me=e=>{const t=e.attr("class");return r(t)&&/\btiny-pageembed\b/.test(t)},ue=e=>{let t=e;for(;t=t.parent;)if(t.attr("data-ephox-embed-iri")||me(t))return!0;return!1},de=(e,t,r)=>{const o=(0,e.options.get)("xss_sanitization"),a=f(e);return M(e.schema,{sanitize:o,validate:a}).parse(r,{context:t})},he=e=>t=>{const r=()=>{t.setEnabled(e.selection.isEditable())};return e.on("NodeChange",r),r(),()=>{e.off("NodeChange",r)}};e.add("media",(e=>((e=>{const t=e.options.register;t("audio_template_callback",{processor:"function"}),t("video_template_callback",{processor:"function"}),t("iframe_template_callback",{processor:"function"}),t("media_live_embeds",{processor:"boolean",default:!0}),t("media_filter_html",{processor:"boolean",default:!0}),t("media_url_resolver",{processor:"function"}),t("media_alt_source",{processor:"boolean",default:!0}),t("media_poster",{processor:"boolean",default:!0}),t("media_dimensions",{processor:"boolean",default:!0})})(e),(e=>{e.addCommand("mceMedia",(()=>{re(e)}))})(e),(e=>{const t=()=>e.execCommand("mceMedia");e.ui.registry.addToggleButton("media",{tooltip:"Insert/edit media",icon:"embed",onAction:t,onSetup:t=>{const r=e.selection;t.setActive(W(r.getNode()));const o=r.selectorChangedWithUnbind("img[data-mce-object],span[data-mce-object],div[data-ephox-embed-iri]",t.setActive).unbind,a=he(e)(t);return()=>{o(),a()}}}),e.ui.registry.addMenuItem("media",{icon:"embed",text:"Media...",onAction:t,onSetup:he(e)})})(e),(e=>{e.on("ResolveName",(e=>{let t;1===e.target.nodeType&&(t=e.target.getAttribute("data-mce-object"))&&(e.name=t)}))})(e),(e=>{e.on("PreInit",(()=>{const{schema:t,serializer:r,parser:o}=e,a=t.getBoolAttrs();l("webkitallowfullscreen mozallowfullscreen".split(" "),(e=>{a[e]={}})),((e,t)=>{const r=m(e);for(let o=0,a=r.length;o{const o=t.getElementRule(r);o&&l(e,(e=>{o.attributes[e]={},o.attributesOrder.push(e)}))})),o.addNodeFilter("iframe,video,audio,object,embed",(e=>t=>{let r,o=t.length;for(;o--;)r=t[o],r.parent&&(r.parent.attr("data-mce-object")||(ae(r)&&v(e)?ue(r)||r.replace(le(e,r)):ue(r)||r.replace(ne(e,r))))})(e)),r.addAttributeFilter("data-mce-object",((t,r)=>{var o;let a=t.length;for(;a--;){const s=t[a];if(!s.parent)continue;const i=s.attr(r),n=new D(i,1);if("audio"!==i){const e=s.attr("class");e&&-1!==e.indexOf("mce-preview-object")&&s.firstChild?n.attr({width:s.firstChild.attr("width"),height:s.firstChild.attr("height")}):n.attr({width:s.attr("width"),height:s.attr("height")})}n.attr({style:s.attr("style")});const c=null!==(o=s.attributes)&&void 0!==o?o:[];let m=c.length;for(;m--;){const e=c[m].name;0===e.indexOf("data-mce-p-")&&n.attr(e.substr(11),c[m].value)}const u=s.attr("data-mce-html");if(u){const t=de(e,i,unescape(u));l(t.children(),(e=>n.append(e)))}s.replace(n)}}))})),e.on("SetContent",(()=>{const t=e.dom;l(t.select("span.mce-preview-object"),(e=>{0===t.select("span.mce-shim",e).length&&t.add(e,"span",{class:"mce-shim"})}))}))})(e),(e=>{e.on("mousedown",(t=>{const r=e.dom.getParent(t.target,".mce-preview-object");r&&"2"===e.dom.getAttrib(r,"data-mce-selected")&&t.stopImmediatePropagation()})),e.on("click keyup touchend",(()=>{const t=e.selection.getNode();t&&e.dom.hasClass(t,"mce-preview-object")&&e.dom.getAttrib(t,"data-mce-selected")&&t.setAttribute("data-mce-selected","2")})),e.on("ObjectResized",(t=>{const r=t.target;if(r.getAttribute("data-mce-object")){let o=r.getAttribute("data-mce-html");o&&(o=unescape(o),r.setAttribute("data-mce-html",escape(U(o,{width:String(t.width),height:String(t.height)},!1,e.schema))))}}))})(e),(e=>({showDialog:()=>{re(e)}}))(e))))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/nonbreaking/plugin.min.js b/apps/web-antd/public/tinymce/plugins/nonbreaking/plugin.min.js new file mode 100644 index 0000000..25f2e0e --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/nonbreaking/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";var n=tinymce.util.Tools.resolve("tinymce.PluginManager");const e=n=>e=>typeof e===n,o=e("boolean"),a=e("number"),t=n=>e=>e.options.get(n),i=t("nonbreaking_force_tab"),s=t("nonbreaking_wrap"),r=(n,e)=>{let o="";for(let a=0;a{const o=s(n)||n.plugins.visualchars?`${r(" ",e)}`:r(" ",e);n.undoManager.transact((()=>n.insertContent(o)))};var l=tinymce.util.Tools.resolve("tinymce.util.VK");const u=n=>e=>{const o=()=>{e.setEnabled(n.selection.isEditable())};return n.on("NodeChange",o),o(),()=>{n.off("NodeChange",o)}};n.add("nonbreaking",(n=>{(n=>{const e=n.options.register;e("nonbreaking_force_tab",{processor:n=>o(n)?{value:n?3:0,valid:!0}:a(n)?{value:n,valid:!0}:{valid:!1,message:"Must be a boolean or number."},default:!1}),e("nonbreaking_wrap",{processor:"boolean",default:!0})})(n),(n=>{n.addCommand("mceNonBreaking",(()=>{c(n,1)}))})(n),(n=>{const e=()=>n.execCommand("mceNonBreaking");n.ui.registry.addButton("nonbreaking",{icon:"non-breaking",tooltip:"Nonbreaking space",onAction:e,onSetup:u(n)}),n.ui.registry.addMenuItem("nonbreaking",{icon:"non-breaking",text:"Nonbreaking space",onAction:e,onSetup:u(n)})})(n),(n=>{const e=i(n);e>0&&n.on("keydown",(o=>{if(o.keyCode===l.TAB&&!o.isDefaultPrevented()){if(o.shiftKey)return;o.preventDefault(),o.stopImmediatePropagation(),c(n,e)}}))})(n)}))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/pagebreak/plugin.min.js b/apps/web-antd/public/tinymce/plugins/pagebreak/plugin.min.js new file mode 100644 index 0000000..a505295 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/pagebreak/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),a=tinymce.util.Tools.resolve("tinymce.Env");const t=e=>a=>a.options.get(e),n=t("pagebreak_separator"),o=t("pagebreak_split_block"),r="mce-pagebreak",s=e=>{const t=``;return e?`

    ${t}

    `:t},c=e=>a=>{const t=()=>{a.setEnabled(e.selection.isEditable())};return e.on("NodeChange",t),t(),()=>{e.off("NodeChange",t)}};e.add("pagebreak",(e=>{(e=>{const a=e.options.register;a("pagebreak_separator",{processor:"string",default:"\x3c!-- pagebreak --\x3e"}),a("pagebreak_split_block",{processor:"boolean",default:!1})})(e),(e=>{e.addCommand("mcePageBreak",(()=>{e.insertContent(s(o(e)))}))})(e),(e=>{const a=()=>e.execCommand("mcePageBreak");e.ui.registry.addButton("pagebreak",{icon:"page-break",tooltip:"Page break",onAction:a,onSetup:c(e)}),e.ui.registry.addMenuItem("pagebreak",{text:"Page break",icon:"page-break",onAction:a,onSetup:c(e)})})(e),(e=>{const a=n(e),t=()=>o(e),c=new RegExp(a.replace(/[\?\.\*\[\]\(\)\{\}\+\^\$\:]/g,(e=>"\\"+e)),"gi");e.on("BeforeSetContent",(e=>{e.content=e.content.replace(c,s(t()))})),e.on("PreInit",(()=>{e.serializer.addNodeFilter("img",(n=>{let o,s,c=n.length;for(;c--;)if(o=n[c],s=o.attr("class"),s&&-1!==s.indexOf(r)){const n=o.parent;if(n&&e.schema.getBlockElements()[n.name]&&t()){n.type=3,n.value=a,n.raw=!0,o.remove();continue}o.type=3,o.value=a,o.raw=!0}}))}))})(e),(e=>{e.on("ResolveName",(a=>{"IMG"===a.target.nodeName&&e.dom.hasClass(a.target,r)&&(a.name="pagebreak")}))})(e)}))}(); \ No newline at end of file diff --git a/apps/web-antd/public/tinymce/plugins/preview/plugin.min.js b/apps/web-antd/public/tinymce/plugins/preview/plugin.min.js new file mode 100644 index 0000000..3096240 --- /dev/null +++ b/apps/web-antd/public/tinymce/plugins/preview/plugin.min.js @@ -0,0 +1,4 @@ +/** + * TinyMCE version 7.2.1 (2024-07-03) + */ +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),t=tinymce.util.Tools.resolve("tinymce.Env"),o=tinymce.util.Tools.resolve("tinymce.util.Tools");const n=e=>t=>t.options.get(e),i=n("content_style"),s=n("content_css_cors"),c=n("body_class"),r=n("body_id");e.add("preview",(e=>{(e=>{e.addCommand("mcePreview",(()=>{(e=>{const n=(e=>{var n;let l="";const a=e.dom.encode,d=null!==(n=i(e))&&void 0!==n?n:"";l+='';const m=s(e)?' crossorigin="anonymous"':"";o.each(e.contentCSS,(t=>{l+='"})),d&&(l+='");const y=r(e),u=c(e),v=' + + diff --git a/apps/web-antd/src/assets/global.css b/apps/web-antd/src/assets/global.css new file mode 100644 index 0000000..3324dd1 --- /dev/null +++ b/apps/web-antd/src/assets/global.css @@ -0,0 +1,41 @@ +body{ + margin: 0; + padding: 0; +} + +/*路由切换动画*/ +.router-fade-enter-active { + transition: all 0.3s cubic-bezier(0.6, 0.5, 0.3, 0.1); +} + +.router-fade-leave-active { + transition: all 0.3s cubic-bezier(0.5, 0.5, 0.5, 0.5); +} + +.router-fade-enter { + transform: translateX(0px); + opacity: 0; +} + +.router-fade-leave-to { + transform: translateX(50px); + opacity: 0; +} + +.amap-sug-result { + z-index: 9999 !important; +} + +.fl { + float: left; +} +.fr { + float: right; +} +.tl{ + text-align: left; +} +.tr{ + text-align: right; +} + diff --git a/apps/web-antd/src/assets/iconfont/iconfont.css b/apps/web-antd/src/assets/iconfont/iconfont.css new file mode 100644 index 0000000..3f92a78 --- /dev/null +++ b/apps/web-antd/src/assets/iconfont/iconfont.css @@ -0,0 +1,331 @@ +@font-face { + font-family: "iconfont"; /* Project id 3538338 */ + src: url('iconfont.woff2?t=1663221768308') format('woff2'), + url('iconfont.woff?t=1663221768308') format('woff'), + url('iconfont.ttf?t=1663221768308') format('truetype'); +} + +[class^="iconfont"],[class*="iconfont"]{ + font-family: "iconfont" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-mn_xiuxi:before { + content: "\e60a"; +} + +.icon-cooperation-full:before { + content: "\e85d"; +} + +.icon-yanfa:before { + content: "\e8e9"; +} + +.icon-yanfaguanli-yangpinjianyan:before { + content: "\e63f"; +} + +.icon-tsm_0108:before { + content: "\e621"; +} + +.icon-dengdaishenhe:before { + content: "\e61f"; +} + +.icon-dengdaizhongbeifen:before { + content: "\e60f"; +} + +.icon-zhuanyi:before { + content: "\e686"; +} + +.icon-quxiao:before { + content: "\e67a"; +} + +.icon-yitongguo:before { + content: "\e601"; +} + +.icon-yijujue:before { + content: "\e602"; +} + +.icon-yiquxiao:before { + content: "\e608"; +} + +.icon-yichexiao:before { + content: "\e609"; +} + +.icon-fanhui-chehui-06:before { + content: "\e81d"; +} + +.icon-zhuanyi-16:before { + content: "\e870"; +} + +.icon-iconfontdianzishu:before { + content: "\e605"; +} + +.icon-iconfontchuzu:before { + content: "\e624"; +} + +.icon-iconfontzhitongche:before { + content: "\e625"; +} + +.icon-iconfontgongjiao:before { + content: "\e626"; +} + +.icon-iconfonttehuijingxuan:before { + content: "\e631"; +} + +.icon-iconfontwifi1:before { + content: "\e632"; +} + +.icon-iconfonterweima:before { + content: "\e60e"; +} + +.icon-iconfontunie64e:before { + content: "\e617"; +} + +.icon-iconfonticon3:before { + content: "\e61d"; +} + +.icon-iconfonticonfontsetting:before { + content: "\e735"; +} + +.icon-iconfontbucket:before { + content: "\e618"; +} + +.icon-iconfontyoujian:before { + content: "\e645"; +} + +.icon-iconfontkafei:before { + content: "\e61e"; +} + +.icon-iconfontihou:before { + content: "\e630"; +} + +.icon-iconfontgift:before { + content: "\e604"; +} + +.icon-iconfontdianying:before { + content: "\e606"; +} + +.icon-iconfontrenmentuijian:before { + content: "\e633"; +} + +.icon-iconfont3:before { + content: "\e607"; +} + +.icon-xiaoxi:before { + content: "\e62b"; +} + +.icon-fangzi:before { + content: "\e646"; +} + +.icon-BBDanquan:before { + content: "\e656"; +} + +.icon-iconfont-jiaoyi:before { + content: "\e68d"; +} + +.icon-iconfonticon-dianyu:before { + content: "\e635"; +} + +.icon-iconfonticon-gaojin:before { + content: "\e638"; +} + +.icon-iconfonticon-nengha:before { + content: "\e63c"; +} + +.icon-iconfonticon-xitong:before { + content: "\e641"; +} + +.icon-iconfontkefu:before { + content: "\e61c"; +} + +.icon-mima:before { + content: "\e648"; +} + +.icon-renlishebao:before { + content: "\e636"; +} + +.icon-bumen:before { + content: "\e758"; +} + +.icon-charutupian:before { + content: "\ec7f"; +} + +.icon-kaoqinguanli:before { + content: "\e610"; +} + +.icon-shenfenzheng:before { + content: "\e614"; +} + +.icon-weizhi:before { + content: "\e64b"; +} + +.icon-24gf-phoneBubble:before { + content: "\e966"; +} + +.icon-kaoqin:before { + content: "\e643"; +} + +.icon-huiyi:before { + content: "\e61b"; +} + +.icon-jiaban:before { + content: "\e637"; +} + +.icon-biaoge:before { + content: "\e665"; +} + +.icon-shiyongwendang:before { + content: "\eb66"; +} + +.icon-duoxuankuang:before { + content: "\e62e"; +} + +.icon-danxuan:before { + content: "\e751"; +} + +.icon-chuzu:before { + content: "\e600"; +} + +.icon-zhaopin:before { + content: "\e647"; +} + +.icon-caiwu:before { + content: "\e67d"; +} + +.icon-caigou:before { + content: "\e887"; +} + +.icon-zhufangbutie:before { + content: "\e68e"; +} + +.icon-wodechanpin:before { + content: "\e679"; +} + +.icon-fapiaoguanli:before { + content: "\e63b"; +} + +.icon-gongzi:before { + content: "\e7e9"; +} + +.icon-zhufangbutiezhanghu:before { + content: "\e60c"; +} + +.icon-weixiu:before { + content: "\e613"; +} + +.icon-yuangonglizhi:before { + content: "\e615"; +} + +.icon-zhaopinguanli:before { + content: "\e616"; +} + +.icon-caiwu1:before { + content: "\e603"; +} + +.icon-qingjiashenqing:before { + content: "\e60d"; +} + +.icon-ziyuan207:before { + content: "\e722"; +} + +.icon-yongcanjiucan:before { + content: "\e67e"; +} + +.icon-map-site:before { + content: "\ea00"; +} + +.icon-hetong:before { + content: "\e68a"; +} + +.icon-buka:before { + content: "\e6ca"; +} + +.icon-chucha:before { + content: "\e6c7"; +} + +.icon-baoxiaoshenqing-feiyongbaoxiaoshenqing-02:before { + content: "\e726"; +} + +.icon-a-11Cfenzuzuzhishu:before { + content: "\e676"; +} + diff --git a/apps/web-antd/src/assets/iconfont/iconfont.json b/apps/web-antd/src/assets/iconfont/iconfont.json new file mode 100644 index 0000000..07f7a59 --- /dev/null +++ b/apps/web-antd/src/assets/iconfont/iconfont.json @@ -0,0 +1,562 @@ +{ + "id": "3538338", + "name": "wflow", + "font_family": "iconfont", + "css_prefix_text": "icon-", + "description": "", + "glyphs": [ + { + "icon_id": "4188525", + "name": "AK-MN_休息", + "font_class": "mn_xiuxi", + "unicode": "e60a", + "unicode_decimal": 58890 + }, + { + "icon_id": "18164281", + "name": "握手,合作,协作", + "font_class": "cooperation-full", + "unicode": "e85d", + "unicode_decimal": 59485 + }, + { + "icon_id": "20508529", + "name": "研发", + "font_class": "yanfa", + "unicode": "e8e9", + "unicode_decimal": 59625 + }, + { + "icon_id": "24161461", + "name": "研发管理-样品检验", + "font_class": "yanfaguanli-yangpinjianyan", + "unicode": "e63f", + "unicode_decimal": 58943 + }, + { + "icon_id": "29119075", + "name": "退货", + "font_class": "tsm_0108", + "unicode": "e621", + "unicode_decimal": 58913 + }, + { + "icon_id": "1125648", + "name": "等待审核", + "font_class": "dengdaishenhe", + "unicode": "e61f", + "unicode_decimal": 58911 + }, + { + "icon_id": "11903016", + "name": "等待中备份", + "font_class": "dengdaizhongbeifen", + "unicode": "e60f", + "unicode_decimal": 58895 + }, + { + "icon_id": "167224", + "name": "转移", + "font_class": "zhuanyi", + "unicode": "e686", + "unicode_decimal": 59014 + }, + { + "icon_id": "867063", + "name": "取消", + "font_class": "quxiao", + "unicode": "e67a", + "unicode_decimal": 59002 + }, + { + "icon_id": "10299093", + "name": "已通过", + "font_class": "yitongguo", + "unicode": "e601", + "unicode_decimal": 58881 + }, + { + "icon_id": "10299097", + "name": "已拒绝", + "font_class": "yijujue", + "unicode": "e602", + "unicode_decimal": 58882 + }, + { + "icon_id": "10299547", + "name": "已取消", + "font_class": "yiquxiao", + "unicode": "e608", + "unicode_decimal": 58888 + }, + { + "icon_id": "12815781", + "name": "已撤销", + "font_class": "yichexiao", + "unicode": "e609", + "unicode_decimal": 58889 + }, + { + "icon_id": "24858654", + "name": "返回-撤回-06", + "font_class": "fanhui-chehui-06", + "unicode": "e81d", + "unicode_decimal": 59421 + }, + { + "icon_id": "24861786", + "name": "转移-16", + "font_class": "zhuanyi-16", + "unicode": "e870", + "unicode_decimal": 59504 + }, + { + "icon_id": "321312", + "name": "iconfont-dianzishu", + "font_class": "iconfontdianzishu", + "unicode": "e605", + "unicode_decimal": 58885 + }, + { + "icon_id": "321522", + "name": "iconfont-chuzu", + "font_class": "iconfontchuzu", + "unicode": "e624", + "unicode_decimal": 58916 + }, + { + "icon_id": "321523", + "name": "iconfont-zhitongche", + "font_class": "iconfontzhitongche", + "unicode": "e625", + "unicode_decimal": 58917 + }, + { + "icon_id": "321524", + "name": "iconfont-gongjiao", + "font_class": "iconfontgongjiao", + "unicode": "e626", + "unicode_decimal": 58918 + }, + { + "icon_id": "324450", + "name": "iconfont-tehuijingxuan", + "font_class": "iconfonttehuijingxuan", + "unicode": "e631", + "unicode_decimal": 58929 + }, + { + "icon_id": "354643", + "name": "wifi", + "font_class": "iconfontwifi1", + "unicode": "e632", + "unicode_decimal": 58930 + }, + { + "icon_id": "372393", + "name": "iconfont-erweima", + "font_class": "iconfonterweima", + "unicode": "e60e", + "unicode_decimal": 58894 + }, + { + "icon_id": "417566", + "name": "iconfont-unie64e", + "font_class": "iconfontunie64e", + "unicode": "e617", + "unicode_decimal": 58903 + }, + { + "icon_id": "461928", + "name": "iconfont-me", + "font_class": "iconfonticon3", + "unicode": "e61d", + "unicode_decimal": 58909 + }, + { + "icon_id": "579060", + "name": "iconfont-iconfontsetting", + "font_class": "iconfonticonfontsetting", + "unicode": "e735", + "unicode_decimal": 59189 + }, + { + "icon_id": "655421", + "name": "iconfont-bucket", + "font_class": "iconfontbucket", + "unicode": "e618", + "unicode_decimal": 58904 + }, + { + "icon_id": "655501", + "name": "iconfont-youjian", + "font_class": "iconfontyoujian", + "unicode": "e645", + "unicode_decimal": 58949 + }, + { + "icon_id": "699106", + "name": "Iconfont-kafei", + "font_class": "iconfontkafei", + "unicode": "e61e", + "unicode_decimal": 58910 + }, + { + "icon_id": "716455", + "name": "iconfont-ihou", + "font_class": "iconfontihou", + "unicode": "e630", + "unicode_decimal": 58928 + }, + { + "icon_id": "859249", + "name": "iconfont-gift", + "font_class": "iconfontgift", + "unicode": "e604", + "unicode_decimal": 58884 + }, + { + "icon_id": "986389", + "name": "iconfont-dianying", + "font_class": "iconfontdianying", + "unicode": "e606", + "unicode_decimal": 58886 + }, + { + "icon_id": "997429", + "name": "iconfont-renmentuijian", + "font_class": "iconfontrenmentuijian", + "unicode": "e633", + "unicode_decimal": 58931 + }, + { + "icon_id": "1163056", + "name": "iconfont-3", + "font_class": "iconfont3", + "unicode": "e607", + "unicode_decimal": 58887 + }, + { + "icon_id": "1215978", + "name": "BBD消息", + "font_class": "xiaoxi", + "unicode": "e62b", + "unicode_decimal": 58923 + }, + { + "icon_id": "1267041", + "name": "BBD房子", + "font_class": "fangzi", + "unicode": "e646", + "unicode_decimal": 58950 + }, + { + "icon_id": "1325666", + "name": "BBD安全", + "font_class": "BBDanquan", + "unicode": "e656", + "unicode_decimal": 58966 + }, + { + "icon_id": "1539713", + "name": "iconfont-jiaoyi", + "font_class": "iconfont-jiaoyi", + "unicode": "e68d", + "unicode_decimal": 59021 + }, + { + "icon_id": "6253872", + "name": icon-dianyu", + "font_class": "iconfonticon-dianyu", + "unicode": "e635", + "unicode_decimal": 58933 + }, + { + "icon_id": "6253876", + "name": icon-gaojin", + "font_class": "iconfonticon-gaojin", + "unicode": "e638", + "unicode_decimal": 58936 + }, + { + "icon_id": "6253885", + "name": icon-nengha", + "font_class": "iconfonticon-nengha", + "unicode": "e63c", + "unicode_decimal": 58940 + }, + { + "icon_id": "6253901", + "name": icon-xitong", + "font_class": "iconfonticon-xitong", + "unicode": "e641", + "unicode_decimal": 58945 + }, + { + "icon_id": "807897", + "name": "iconfont-kefu", + "font_class": "iconfontkefu", + "unicode": "e61c", + "unicode_decimal": 58908 + }, + { + "icon_id": "1313126", + "name": "BBD密码", + "font_class": "mima", + "unicode": "e648", + "unicode_decimal": 58952 + }, + { + "icon_id": "2131309", + "name": "人力社保", + "font_class": "renlishebao", + "unicode": "e636", + "unicode_decimal": 58934 + }, + { + "icon_id": "4774868", + "name": "部门", + "font_class": "bumen", + "unicode": "e758", + "unicode_decimal": 59224 + }, + { + "icon_id": "6337457", + "name": "插入图片", + "font_class": "charutupian", + "unicode": "ec7f", + "unicode_decimal": 60543 + }, + { + "icon_id": "2958951", + "name": "考勤管理", + "font_class": "kaoqinguanli", + "unicode": "e610", + "unicode_decimal": 58896 + }, + { + "icon_id": "3007689", + "name": "身份证", + "font_class": "shenfenzheng", + "unicode": "e614", + "unicode_decimal": 58900 + }, + { + "icon_id": "5121522", + "name": "位置", + "font_class": "weizhi", + "unicode": "e64b", + "unicode_decimal": 58955 + }, + { + "icon_id": "7568869", + "name": "24gf-phoneBubble", + "font_class": "24gf-phoneBubble", + "unicode": "e966", + "unicode_decimal": 59750 + }, + { + "icon_id": "11134714", + "name": "考勤", + "font_class": "kaoqin", + "unicode": "e643", + "unicode_decimal": 58947 + }, + { + "icon_id": "15972093", + "name": "会议", + "font_class": "huiyi", + "unicode": "e61b", + "unicode_decimal": 58907 + }, + { + "icon_id": "19883444", + "name": "加班", + "font_class": "jiaban", + "unicode": "e637", + "unicode_decimal": 58935 + }, + { + "icon_id": "1392555", + "name": "表格", + "font_class": "biaoge", + "unicode": "e665", + "unicode_decimal": 58981 + }, + { + "icon_id": "3868276", + "name": "使用文档", + "font_class": "shiyongwendang", + "unicode": "eb66", + "unicode_decimal": 60262 + }, + { + "icon_id": "5881147", + "name": "多选框", + "font_class": "duoxuankuang", + "unicode": "e62e", + "unicode_decimal": 58926 + }, + { + "icon_id": "26323690", + "name": "单选", + "font_class": "danxuan", + "unicode": "e751", + "unicode_decimal": 59217 + }, + { + "icon_id": "5032", + "name": "出租", + "font_class": "chuzu", + "unicode": "e600", + "unicode_decimal": 58880 + }, + { + "icon_id": "1079372", + "name": "招聘", + "font_class": "zhaopin", + "unicode": "e647", + "unicode_decimal": 58951 + }, + { + "icon_id": "1183143", + "name": "财务", + "font_class": "caiwu", + "unicode": "e67d", + "unicode_decimal": 59005 + }, + { + "icon_id": "1727267", + "name": "05采购", + "font_class": "caigou", + "unicode": "e887", + "unicode_decimal": 59527 + }, + { + "icon_id": "1841799", + "name": "住房补贴", + "font_class": "zhufangbutie", + "unicode": "e68e", + "unicode_decimal": 59022 + }, + { + "icon_id": "1876349", + "name": "我的产品", + "font_class": "wodechanpin", + "unicode": "e679", + "unicode_decimal": 59001 + }, + { + "icon_id": "1977843", + "name": "发票管理", + "font_class": "fapiaoguanli", + "unicode": "e63b", + "unicode_decimal": 58939 + }, + { + "icon_id": "7790995", + "name": "工资", + "font_class": "gongzi", + "unicode": "e7e9", + "unicode_decimal": 59369 + }, + { + "icon_id": "10120009", + "name": "住房补贴账户", + "font_class": "zhufangbutiezhanghu", + "unicode": "e60c", + "unicode_decimal": 58892 + }, + { + "icon_id": "11435446", + "name": "维修", + "font_class": "weixiu", + "unicode": "e613", + "unicode_decimal": 58899 + }, + { + "icon_id": "11435453", + "name": "员工离职", + "font_class": "yuangonglizhi", + "unicode": "e615", + "unicode_decimal": 58901 + }, + { + "icon_id": "11435456", + "name": "招聘管理", + "font_class": "zhaopinguanli", + "unicode": "e616", + "unicode_decimal": 58902 + }, + { + "icon_id": "12911861", + "name": "财务", + "font_class": "caiwu1", + "unicode": "e603", + "unicode_decimal": 58883 + }, + { + "icon_id": "14443545", + "name": "请假申请", + "font_class": "qingjiashenqing", + "unicode": "e60d", + "unicode_decimal": 58893 + }, + { + "icon_id": "14947326", + "name": "出差", + "font_class": "ziyuan207", + "unicode": "e722", + "unicode_decimal": 59170 + }, + { + "icon_id": "17187052", + "name": "用餐就餐", + "font_class": "yongcanjiucan", + "unicode": "e67e", + "unicode_decimal": 59006 + }, + { + "icon_id": "18170995", + "name": "地图组织站点,层级,下级,组织架构布局", + "font_class": "map-site", + "unicode": "ea00", + "unicode_decimal": 59904 + }, + { + "icon_id": "21053836", + "name": "合同", + "font_class": "hetong", + "unicode": "e68a", + "unicode_decimal": 59018 + }, + { + "icon_id": "21159370", + "name": "补卡", + "font_class": "buka", + "unicode": "e6ca", + "unicode_decimal": 59082 + }, + { + "icon_id": "24080655", + "name": "出差", + "font_class": "chucha", + "unicode": "e6c7", + "unicode_decimal": 59079 + }, + { + "icon_id": "24283254", + "name": "报销申请-费用报销申请-02", + "font_class": "baoxiaoshenqing-feiyongbaoxiaoshenqing-02", + "unicode": "e726", + "unicode_decimal": 59174 + }, + { + "icon_id": "29522596", + "name": "11C分组,组织树", + "font_class": "a-11Cfenzuzuzhishu", + "unicode": "e676", + "unicode_decimal": 58998 + } + ] +} diff --git a/apps/web-antd/src/assets/iconfont/iconfont.ttf b/apps/web-antd/src/assets/iconfont/iconfont.ttf new file mode 100644 index 0000000..cb17132 Binary files /dev/null and b/apps/web-antd/src/assets/iconfont/iconfont.ttf differ diff --git a/apps/web-antd/src/assets/iconfont/iconfont.woff b/apps/web-antd/src/assets/iconfont/iconfont.woff new file mode 100644 index 0000000..e7c2947 Binary files /dev/null and b/apps/web-antd/src/assets/iconfont/iconfont.woff differ diff --git a/apps/web-antd/src/assets/iconfont/iconfont.woff2 b/apps/web-antd/src/assets/iconfont/iconfont.woff2 new file mode 100644 index 0000000..57b2798 Binary files /dev/null and b/apps/web-antd/src/assets/iconfont/iconfont.woff2 differ diff --git a/apps/web-antd/src/assets/image/agree.png b/apps/web-antd/src/assets/image/agree.png new file mode 100644 index 0000000..2204d9d Binary files /dev/null and b/apps/web-antd/src/assets/image/agree.png differ diff --git a/apps/web-antd/src/assets/image/branch.png b/apps/web-antd/src/assets/image/branch.png new file mode 100644 index 0000000..220189a Binary files /dev/null and b/apps/web-antd/src/assets/image/branch.png differ diff --git a/apps/web-antd/src/assets/image/cc.png b/apps/web-antd/src/assets/image/cc.png new file mode 100644 index 0000000..bc9688a Binary files /dev/null and b/apps/web-antd/src/assets/image/cc.png differ diff --git a/apps/web-antd/src/assets/image/code.png b/apps/web-antd/src/assets/image/code.png new file mode 100644 index 0000000..91ab1d5 Binary files /dev/null and b/apps/web-antd/src/assets/image/code.png differ diff --git a/apps/web-antd/src/assets/image/diannao.png b/apps/web-antd/src/assets/image/diannao.png new file mode 100644 index 0000000..0ac9509 Binary files /dev/null and b/apps/web-antd/src/assets/image/diannao.png differ diff --git a/apps/web-antd/src/assets/image/huojian.png b/apps/web-antd/src/assets/image/huojian.png new file mode 100644 index 0000000..4f1d885 Binary files /dev/null and b/apps/web-antd/src/assets/image/huojian.png differ diff --git a/apps/web-antd/src/assets/image/inclusive.png b/apps/web-antd/src/assets/image/inclusive.png new file mode 100644 index 0000000..eaa451e Binary files /dev/null and b/apps/web-antd/src/assets/image/inclusive.png differ diff --git a/apps/web-antd/src/assets/image/logo.png b/apps/web-antd/src/assets/image/logo.png new file mode 100644 index 0000000..7db7107 Binary files /dev/null and b/apps/web-antd/src/assets/image/logo.png differ diff --git a/apps/web-antd/src/assets/image/pending.png b/apps/web-antd/src/assets/image/pending.png new file mode 100644 index 0000000..c35d548 Binary files /dev/null and b/apps/web-antd/src/assets/image/pending.png differ diff --git a/apps/web-antd/src/assets/image/quanxian.png b/apps/web-antd/src/assets/image/quanxian.png new file mode 100644 index 0000000..684fa63 Binary files /dev/null and b/apps/web-antd/src/assets/image/quanxian.png differ diff --git a/apps/web-antd/src/assets/image/recall.png b/apps/web-antd/src/assets/image/recall.png new file mode 100644 index 0000000..81be1b2 Binary files /dev/null and b/apps/web-antd/src/assets/image/recall.png differ diff --git a/apps/web-antd/src/assets/image/refuse.png b/apps/web-antd/src/assets/image/refuse.png new file mode 100644 index 0000000..de87bb7 Binary files /dev/null and b/apps/web-antd/src/assets/image/refuse.png differ diff --git a/apps/web-antd/src/assets/image/shouru.png b/apps/web-antd/src/assets/image/shouru.png new file mode 100644 index 0000000..87dd6b9 Binary files /dev/null and b/apps/web-antd/src/assets/image/shouru.png differ diff --git a/apps/web-antd/src/assets/image/submit.png b/apps/web-antd/src/assets/image/submit.png new file mode 100644 index 0000000..8e62280 Binary files /dev/null and b/apps/web-antd/src/assets/image/submit.png differ diff --git a/apps/web-antd/src/assets/image/wenjian.png b/apps/web-antd/src/assets/image/wenjian.png new file mode 100644 index 0000000..efba407 Binary files /dev/null and b/apps/web-antd/src/assets/image/wenjian.png differ diff --git a/apps/web-antd/src/assets/image/zhichu.png b/apps/web-antd/src/assets/image/zhichu.png new file mode 100644 index 0000000..4f2f9c1 Binary files /dev/null and b/apps/web-antd/src/assets/image/zhichu.png differ diff --git a/apps/web-antd/src/assets/logo.png b/apps/web-antd/src/assets/logo.png new file mode 100644 index 0000000..b27828f Binary files /dev/null and b/apps/web-antd/src/assets/logo.png differ diff --git a/apps/web-antd/src/assets/ptrw.png b/apps/web-antd/src/assets/ptrw.png new file mode 100644 index 0000000..bbf331f Binary files /dev/null and b/apps/web-antd/src/assets/ptrw.png differ diff --git a/apps/web-antd/src/assets/theme.less b/apps/web-antd/src/assets/theme.less new file mode 100644 index 0000000..7626351 --- /dev/null +++ b/apps/web-antd/src/assets/theme.less @@ -0,0 +1,62 @@ +//主题定制 + +@theme-primary: #1989fa; //主题色,应当与element-ui一致 +@theme-danger: #f56c6c; //主题色,应当与element-ui一致 +@theme-success: #35b881; +@theme-warning: #f78f5f; +@theme-aside-bgc: #f7f7f9; + +@theme-desc-color: #a2a4a8; //辅助文字颜色 +@theme-secondary: #909399; // + +//审批流程节点配色 +@node-root: #576a95; //发起人 +@node-condition: #15bca3; //条件 +@node-cc: #3296fa; //抄送 +@node-concurrent: #718dff; //并行 +@node-approval: #ff943e; //审批 +@node-delay: #f25643; //延时 +@node-trigger: #47bc82; //触发器 + + +//移动端Vant Less覆盖 +@font-size-xs: 12px; +@font-size-sm: 14px; +@font-size-md: 16px; +@font-size-lg: 18px; +@font-weight-bold: 500; +@line-height-xs: 16px; +@line-height-sm: 20px; +@line-height-md: 22px; +@line-height-lg: 24px; + +//通用类 +.w-desc-text{ + color: @theme-desc-color; +} + +.w-row-text{ + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.w-t-center{ + text-align: left; +} + +.w-t-left{ + left: 0; + text-align: left; +} + +.w-t-right{ + right: 0; + text-align: left; +} + +.w-h-center{ + display: flex; + align-items: center; +} + diff --git a/apps/web-antd/src/assets/微信图片_20250612095245.png b/apps/web-antd/src/assets/微信图片_20250612095245.png new file mode 100644 index 0000000..a97f1df Binary files /dev/null and b/apps/web-antd/src/assets/微信图片_20250612095245.png differ diff --git a/apps/web-antd/src/bootstrap.ts b/apps/web-antd/src/bootstrap.ts new file mode 100644 index 0000000..7120ae5 --- /dev/null +++ b/apps/web-antd/src/bootstrap.ts @@ -0,0 +1,113 @@ +/* + * @Author: Mm + * @Date: 2025-05-08 17:46:47 + * @LastEditors: Mm + * @LastEditTime: 2025-06-18 10:46:57 + * 不写bug的程序员不是一个好程序员! + */ +/* + * @Author: Mm + * @Date: 2025-05-06 14:08:27 + * @LastEditors: Mm + * @LastEditTime: 2025-05-26 09:22:40 + * 不写bug的程序员不是一个好程序员! + */ +import { createApp, watchEffect } from 'vue'; + +import { registerAccessDirective } from '@vben/access'; +import { initTippy } from '@vben/common-ui'; +import { preferences } from '@vben/preferences'; +import { initStores } from '@vben/stores'; +import '@vben/styles'; +import '@vben/styles/antd'; + +import { useTitle } from '@vueuse/core'; + +import { setupGlobalComponent } from '#/components/global'; +import { $t, setupI18n } from '#/locales'; + +import { initComponentAdapter } from './adapter/component'; +import App from './app.vue'; +import { router } from './router'; +import vuexStore from './store/vuex.js' +import utils from './utils/utils.js' +import ElementPlus from 'element-plus'; +import 'element-plus/dist/index.css'; +import '#/assets/global.css'; +import '#/assets/theme.less'; +import 'vant/lib/index.css'; +import * as ElementPlusIconsVue from '@element-plus/icons-vue'; +import Ellipsis from '#/components/common/Ellipsis.vue'; +import WDialog from '#/components/common/WDialog.vue'; +import Tip from '#/components/common/Tip.vue'; +import Avatar from '#/components/common/Avatar.vue'; +//新增导入 addAPIProvider +import { Icon, addAPIProvider } from '@iconify/vue'; + +//注册自定义图标服务地址 +const iconifyAPI = import.meta.env.VITE_ICONIFY_API || 'http://53.1.252.95:8080'; +addAPIProvider('', { + resources: [iconifyAPI], +}); + +async function bootstrap(namespace: string) { + // 初始化组件适配器 + await initComponentAdapter(); + + // // 设置弹窗的默认配置 + // setDefaultModalProps({ + // fullscreenButton: false, + // }); + // // 设置抽屉的默认配置 + // setDefaultDrawerProps({ + // zIndex: 1020, + // }); + + const app = createApp(App); + app.use(ElementPlus); + for (const [key, component] of Object.entries(ElementPlusIconsVue)) { + app.component(key, component); + } + app.component('Icon', Icon); + app.component('iconify', Icon); + app.component('Ellipsis', Ellipsis) + app.component('WDialog', WDialog) + app.component('Tip', Tip) + app.component('w-avatar', Avatar); + + + // 全局组件 + setupGlobalComponent(app); + + // 国际化 i18n 配置 + await setupI18n(app); + + // 配置 pinia-tore + await initStores(app, { namespace }); + + // 安装权限指令 + registerAccessDirective(app); + + // 初始化 tippy + initTippy(app); + + // 配置路由及路由守卫 + app.use(router); + + app.use(vuexStore) + app.use(utils); + + // 动态更新标题 + watchEffect(() => { + if (preferences.app.dynamicTitle) { + const routeTitle = router.currentRoute.value.meta?.title; + const pageTitle = + (routeTitle ? `${$t(routeTitle)} - ` : '') + preferences.app.name; + useTitle(pageTitle); + } + }); + + app.mount('#app'); +} + +export { bootstrap }; diff --git a/apps/web-antd/src/components/common/Avatar.vue b/apps/web-antd/src/components/common/Avatar.vue new file mode 100644 index 0000000..259a0ff --- /dev/null +++ b/apps/web-antd/src/components/common/Avatar.vue @@ -0,0 +1,207 @@ + + + + + diff --git a/apps/web-antd/src/components/common/CodeEditor.vue b/apps/web-antd/src/components/common/CodeEditor.vue new file mode 100644 index 0000000..202fd1a --- /dev/null +++ b/apps/web-antd/src/components/common/CodeEditor.vue @@ -0,0 +1,149 @@ + + + diff --git a/apps/web-antd/src/components/common/Editor.vue b/apps/web-antd/src/components/common/Editor.vue new file mode 100644 index 0000000..c582805 --- /dev/null +++ b/apps/web-antd/src/components/common/Editor.vue @@ -0,0 +1,225 @@ + + + + + + diff --git a/apps/web-antd/src/components/common/Ellipsis.vue b/apps/web-antd/src/components/common/Ellipsis.vue new file mode 100644 index 0000000..5684cba --- /dev/null +++ b/apps/web-antd/src/components/common/Ellipsis.vue @@ -0,0 +1,53 @@ + + + + + diff --git a/apps/web-antd/src/components/common/FormItem.vue b/apps/web-antd/src/components/common/FormItem.vue new file mode 100644 index 0000000..e90661f --- /dev/null +++ b/apps/web-antd/src/components/common/FormItem.vue @@ -0,0 +1,104 @@ + + + + + diff --git a/apps/web-antd/src/components/common/HttpReq.vue b/apps/web-antd/src/components/common/HttpReq.vue new file mode 100644 index 0000000..84c65f4 --- /dev/null +++ b/apps/web-antd/src/components/common/HttpReq.vue @@ -0,0 +1,197 @@ + + + + + diff --git a/apps/web-antd/src/components/common/Icon.vue b/apps/web-antd/src/components/common/Icon.vue new file mode 100644 index 0000000..dabbfd6 --- /dev/null +++ b/apps/web-antd/src/components/common/Icon.vue @@ -0,0 +1,60 @@ + + + diff --git a/apps/web-antd/src/components/common/OrgPicker.vue b/apps/web-antd/src/components/common/OrgPicker.vue new file mode 100644 index 0000000..0edc3d8 --- /dev/null +++ b/apps/web-antd/src/components/common/OrgPicker.vue @@ -0,0 +1,592 @@ + + + + + + diff --git a/apps/web-antd/src/components/common/ProcessStatus.vue b/apps/web-antd/src/components/common/ProcessStatus.vue new file mode 100644 index 0000000..8626825 --- /dev/null +++ b/apps/web-antd/src/components/common/ProcessStatus.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/apps/web-antd/src/components/common/ScanCode.vue b/apps/web-antd/src/components/common/ScanCode.vue new file mode 100644 index 0000000..47c1c36 --- /dev/null +++ b/apps/web-antd/src/components/common/ScanCode.vue @@ -0,0 +1,149 @@ + + + + + diff --git a/apps/web-antd/src/components/common/Tip.vue b/apps/web-antd/src/components/common/Tip.vue new file mode 100644 index 0000000..2b3f74c --- /dev/null +++ b/apps/web-antd/src/components/common/Tip.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/apps/web-antd/src/components/common/WDialog.vue b/apps/web-antd/src/components/common/WDialog.vue new file mode 100644 index 0000000..0bd63fe --- /dev/null +++ b/apps/web-antd/src/components/common/WDialog.vue @@ -0,0 +1,134 @@ + + + + + diff --git a/apps/web-antd/src/components/cropper/index.ts b/apps/web-antd/src/components/cropper/index.ts new file mode 100644 index 0000000..6afad01 --- /dev/null +++ b/apps/web-antd/src/components/cropper/index.ts @@ -0,0 +1,3 @@ +export { default as CropperAvatar } from './src/cropper-avatar.vue'; +export { default as CropperImage } from './src/cropper.vue'; +export type { Cropper } from './src/typing'; diff --git a/apps/web-antd/src/components/cropper/src/cropper-avatar.vue b/apps/web-antd/src/components/cropper/src/cropper-avatar.vue new file mode 100644 index 0000000..a29aab2 --- /dev/null +++ b/apps/web-antd/src/components/cropper/src/cropper-avatar.vue @@ -0,0 +1,170 @@ + + + + diff --git a/apps/web-antd/src/components/cropper/src/cropper-modal.vue b/apps/web-antd/src/components/cropper/src/cropper-modal.vue new file mode 100644 index 0000000..dc59195 --- /dev/null +++ b/apps/web-antd/src/components/cropper/src/cropper-modal.vue @@ -0,0 +1,375 @@ + + + + diff --git a/apps/web-antd/src/components/cropper/src/cropper.vue b/apps/web-antd/src/components/cropper/src/cropper.vue new file mode 100644 index 0000000..4089ba3 --- /dev/null +++ b/apps/web-antd/src/components/cropper/src/cropper.vue @@ -0,0 +1,196 @@ + + + diff --git a/apps/web-antd/src/components/cropper/src/typing.ts b/apps/web-antd/src/components/cropper/src/typing.ts new file mode 100644 index 0000000..e76cc6f --- /dev/null +++ b/apps/web-antd/src/components/cropper/src/typing.ts @@ -0,0 +1,8 @@ +import type Cropper from 'cropperjs'; + +export interface CropendResult { + imgBase64: string; + imgInfo: Cropper.Data; +} + +export type { Cropper }; diff --git a/apps/web-antd/src/components/description/index.ts b/apps/web-antd/src/components/description/index.ts new file mode 100644 index 0000000..46a851f --- /dev/null +++ b/apps/web-antd/src/components/description/index.ts @@ -0,0 +1,3 @@ +export { default as Description } from './src/description.vue'; +export * from './src/typing'; +export { useDescription } from './src/useDescription'; diff --git a/apps/web-antd/src/components/description/src/description.vue b/apps/web-antd/src/components/description/src/description.vue new file mode 100644 index 0000000..2398f35 --- /dev/null +++ b/apps/web-antd/src/components/description/src/description.vue @@ -0,0 +1,202 @@ + diff --git a/apps/web-antd/src/components/description/src/typing.ts b/apps/web-antd/src/components/description/src/typing.ts new file mode 100644 index 0000000..204b697 --- /dev/null +++ b/apps/web-antd/src/components/description/src/typing.ts @@ -0,0 +1,48 @@ +import type { DescriptionsProps } from 'ant-design-vue/es/descriptions'; +import type { JSX } from 'vue/jsx-runtime'; + +import type { CSSProperties, VNode } from 'vue'; + +import type { Recordable } from '@vben/types'; + +export interface DescItem { + labelMinWidth?: number; + contentMinWidth?: number; + labelStyle?: CSSProperties; + field: string; + label: JSX.Element | string | VNode; + // Merge column + span?: number; + show?: (...arg: any) => boolean; + // render + render?: ( + val: any, + data: Recordable, + ) => Element | JSX.Element | number | string | undefined | VNode; +} + +export interface DescriptionProps extends DescriptionsProps { + // Whether to include the collapse component + useCollapse?: boolean; + /** + * item configuration + * @type DescItem + */ + schema: DescItem[]; + /** + * 数据 + * @type object + */ + data: Recordable; +} + +export interface DescInstance { + setDescProps(descProps: Partial, delay?: boolean): void; +} + +export type Register = (descInstance: DescInstance) => void; + +/** + * @description: + */ +export type UseDescReturnType = [Register, DescInstance]; diff --git a/apps/web-antd/src/components/description/src/useDescription.ts b/apps/web-antd/src/components/description/src/useDescription.ts new file mode 100644 index 0000000..477756b --- /dev/null +++ b/apps/web-antd/src/components/description/src/useDescription.ts @@ -0,0 +1,44 @@ +import type { + DescInstance, + DescriptionProps, + UseDescReturnType, +} from './typing'; + +import { getCurrentInstance, ref, unref } from 'vue'; + +export function useDescription( + props?: Partial, +): UseDescReturnType { + if (!getCurrentInstance()) { + throw new Error( + 'useDescription() can only be used inside setup() or functional components!', + ); + } + const desc = ref(null); + const loaded = ref(false); + + function register(instance: DescInstance) { + // if (unref(loaded) && import.meta.env.PROD) { + // return; + // } + desc.value = instance; + props && instance.setDescProps(props); + loaded.value = true; + } + + const methods: DescInstance = { + setDescProps: ( + descProps: Partial, + delay = false, + ): void => { + if (!delay) { + unref(desc)?.setDescProps(descProps); + return; + } + // 奇怪的问题 在modal中需要setTimeout才会生效 + setTimeout(() => unref(desc)?.setDescProps(descProps)); + }, + }; + + return [register, methods]; +} diff --git a/apps/web-antd/src/components/dict/index.ts b/apps/web-antd/src/components/dict/index.ts new file mode 100644 index 0000000..08282cf --- /dev/null +++ b/apps/web-antd/src/components/dict/index.ts @@ -0,0 +1,2 @@ +export { tagSelectOptions, tagTypes } from './src/data'; +export { default as DictTag } from './src/index.vue'; diff --git a/apps/web-antd/src/components/dict/src/data.tsx b/apps/web-antd/src/components/dict/src/data.tsx new file mode 100644 index 0000000..7033c25 --- /dev/null +++ b/apps/web-antd/src/components/dict/src/data.tsx @@ -0,0 +1,44 @@ +import type { VNode } from 'vue'; + +import { Tag } from 'ant-design-vue'; + +interface TagType { + [key: string]: { color: string; label: string }; +} + +export const tagTypes: TagType = { + cyan: { color: 'cyan', label: 'cyan' }, + danger: { color: 'error', label: '危险(danger)' }, + /** 由于和elementUI不同 用于替换颜色 */ + default: { color: 'default', label: '默认(default)' }, + green: { color: 'green', label: 'green' }, + info: { color: 'default', label: '信息(info)' }, + orange: { color: 'orange', label: 'orange' }, + /** 自定义预设 color可以为16进制颜色 */ + pink: { color: 'pink', label: 'pink' }, + primary: { color: 'processing', label: '主要(primary)' }, + purple: { color: 'purple', label: 'purple' }, + red: { color: 'red', label: 'red' }, + success: { color: 'success', label: '成功(success)' }, + warning: { color: 'warning', label: '警告(warning)' }, +}; + +// 字典选择使用 { label: string; value: string }[] +interface Options { + label: string | VNode; + value: string; +} + +export function tagSelectOptions() { + const selectArray: Options[] = []; + Object.keys(tagTypes).forEach((key) => { + if (!tagTypes[key]) return; + const label = tagTypes[key].label; + const color = tagTypes[key].color; + selectArray.push({ + label: {label}, + value: key, + }); + }); + return selectArray; +} diff --git a/apps/web-antd/src/components/dict/src/index.vue b/apps/web-antd/src/components/dict/src/index.vue new file mode 100644 index 0000000..a909cd8 --- /dev/null +++ b/apps/web-antd/src/components/dict/src/index.vue @@ -0,0 +1,58 @@ + + + + + diff --git a/apps/web-antd/src/components/global/button.ts b/apps/web-antd/src/components/global/button.ts new file mode 100644 index 0000000..397fdf6 --- /dev/null +++ b/apps/web-antd/src/components/global/button.ts @@ -0,0 +1,21 @@ +import { defineComponent, h } from 'vue'; + +import { Button } from 'ant-design-vue'; +import buttonProps from 'ant-design-vue/es/button/buttonTypes'; +import { omit } from 'lodash-es'; + +/** + * 表格操作列按钮专用 + */ +export const GhostButton = defineComponent({ + name: 'GhostButton', + props: omit(buttonProps(), ['type', 'ghost', 'size']), + setup(props, { attrs, slots }) { + return () => + h( + Button, + { ...props, ...attrs, type: 'primary', ghost: true, size: 'small' }, + slots, + ); + }, +}); diff --git a/apps/web-antd/src/components/global/index.ts b/apps/web-antd/src/components/global/index.ts new file mode 100644 index 0000000..ff07852 --- /dev/null +++ b/apps/web-antd/src/components/global/index.ts @@ -0,0 +1,14 @@ +import type { App } from 'vue'; + +import { Button as AButton } from 'ant-design-vue'; + +import { GhostButton } from './button'; + +/** + * 全局组件注册 + */ +export function setupGlobalComponent(app: App) { + app.use(AButton); + // 表格操作列专用按钮 + app.component('GhostButton', GhostButton); +} diff --git a/apps/web-antd/src/components/table/index.ts b/apps/web-antd/src/components/table/index.ts new file mode 100644 index 0000000..03a735c --- /dev/null +++ b/apps/web-antd/src/components/table/index.ts @@ -0,0 +1,2 @@ +export { default as OptionsTag } from './src/options-tag.vue'; +export { default as TableSwitch } from './src/table-switch.vue'; diff --git a/apps/web-antd/src/components/table/src/options-tag.vue b/apps/web-antd/src/components/table/src/options-tag.vue new file mode 100644 index 0000000..c7c6891 --- /dev/null +++ b/apps/web-antd/src/components/table/src/options-tag.vue @@ -0,0 +1,21 @@ + + + diff --git a/apps/web-antd/src/components/table/src/table-switch.vue b/apps/web-antd/src/components/table/src/table-switch.vue new file mode 100644 index 0000000..036e984 --- /dev/null +++ b/apps/web-antd/src/components/table/src/table-switch.vue @@ -0,0 +1,84 @@ + + + diff --git a/apps/web-antd/src/components/tenant-toggle/index.ts b/apps/web-antd/src/components/tenant-toggle/index.ts new file mode 100644 index 0000000..d82e602 --- /dev/null +++ b/apps/web-antd/src/components/tenant-toggle/index.ts @@ -0,0 +1 @@ +export { default as TenantToggle } from './src/index.vue'; diff --git a/apps/web-antd/src/components/tenant-toggle/src/index.vue b/apps/web-antd/src/components/tenant-toggle/src/index.vue new file mode 100644 index 0000000..a3398f0 --- /dev/null +++ b/apps/web-antd/src/components/tenant-toggle/src/index.vue @@ -0,0 +1,119 @@ + + + + + diff --git a/apps/web-antd/src/components/tinymce/index.ts b/apps/web-antd/src/components/tinymce/index.ts new file mode 100644 index 0000000..6fe8074 --- /dev/null +++ b/apps/web-antd/src/components/tinymce/index.ts @@ -0,0 +1 @@ +export { default as Tinymce } from './src/editor.vue'; diff --git a/apps/web-antd/src/components/tinymce/src/editor.vue b/apps/web-antd/src/components/tinymce/src/editor.vue new file mode 100644 index 0000000..3441e84 --- /dev/null +++ b/apps/web-antd/src/components/tinymce/src/editor.vue @@ -0,0 +1,384 @@ + + + + + + + diff --git a/apps/web-antd/src/components/tinymce/src/helper.ts b/apps/web-antd/src/components/tinymce/src/helper.ts new file mode 100644 index 0000000..1f98dda --- /dev/null +++ b/apps/web-antd/src/components/tinymce/src/helper.ts @@ -0,0 +1,85 @@ +const validEvents = new Set([ + 'onActivate', + 'onAddUndo', + 'onBeforeAddUndo', + 'onBeforeExecCommand', + 'onBeforeGetContent', + 'onBeforePaste', + 'onBeforeRenderUI', + 'onBeforeSetContent', + 'onBlur', + 'onChange', + 'onClearUndos', + 'onClick', + 'onContextMenu', + 'onCopy', + 'onCut', + 'onDblclick', + 'onDeactivate', + 'onDirty', + 'onDrag', + 'onDragDrop', + 'onDragEnd', + 'onDragGesture', + 'onDragOver', + 'onDrop', + 'onExecCommand', + 'onFocus', + 'onFocusIn', + 'onFocusOut', + 'onGetContent', + 'onHide', + 'onInit', + 'onKeyDown', + 'onKeyPress', + 'onKeyUp', + 'onLoadContent', + 'onMouseDown', + 'onMouseEnter', + 'onMouseLeave', + 'onMouseMove', + 'onMouseOut', + 'onMouseOver', + 'onMouseUp', + 'onNodeChange', + 'onObjectResized', + 'onObjectResizeStart', + 'onObjectSelected', + 'onPaste', + 'onPostProcess', + 'onPostRender', + 'onPreProcess', + 'onProgressState', + 'onRedo', + 'onRemove', + 'onReset', + 'onSaveContent', + 'onSelectionChange', + 'onSetAttrib', + 'onSetContent', + 'onShow', + 'onSubmit', + 'onUndo', + 'onVisualAid', +]); + +const isValidKey = (key: string) => validEvents.has(key); + +export const bindHandlers = ( + initEvent: Event, + listeners: any, + editor: any, +): void => { + Object.keys(listeners) + .filter((element) => isValidKey(element)) + .forEach((key: string) => { + const handler = listeners[key]; + if (typeof handler === 'function') { + if (key === 'onInit') { + handler(initEvent, editor); + } else { + editor.on(key.slice(2), (e: any) => handler(e, editor)); + } + } + }); +}; diff --git a/apps/web-antd/src/components/tinymce/src/img-upload.vue b/apps/web-antd/src/components/tinymce/src/img-upload.vue new file mode 100644 index 0000000..0d7a2be --- /dev/null +++ b/apps/web-antd/src/components/tinymce/src/img-upload.vue @@ -0,0 +1,115 @@ + + + + diff --git a/apps/web-antd/src/components/tinymce/src/tinymce.ts b/apps/web-antd/src/components/tinymce/src/tinymce.ts new file mode 100644 index 0000000..eb3964a --- /dev/null +++ b/apps/web-antd/src/components/tinymce/src/tinymce.ts @@ -0,0 +1,11 @@ +// Any plugins you want to setting has to be imported +// Detail plugins list see https://www.tinymce.com/docs/plugins/ +// Custom builds see https://www.tinymce.com/download/custom-builds/ +// colorpicker/contextmenu/textcolor plugin is now built in to the core editor, please remove it from your editor configuration + +// quickbars 快捷栏 +export const plugins = + 'preview importcss searchreplace autolink autosave save directionality code visualblocks visualchars fullscreen image link media codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount help charmap emoticons accordion'; + +export const toolbar = + 'undo redo | accordion accordionremove | blocks fontfamily fontsize | bold italic underline strikethrough | align numlist bullist | link image | table media | lineheight outdent indent| forecolor backcolor removeformat | charmap emoticons | code fullscreen preview | save print | pagebreak anchor codesample | ltr rtl'; diff --git a/apps/web-antd/src/components/tree/index.ts b/apps/web-antd/src/components/tree/index.ts new file mode 100644 index 0000000..f142790 --- /dev/null +++ b/apps/web-antd/src/components/tree/index.ts @@ -0,0 +1,2 @@ +export { default as MenuSelectTable } from './src/menu-select-table.vue'; +export { default as TreeSelectPanel } from './src/tree-select-panel.vue'; diff --git a/apps/web-antd/src/components/tree/src/data.tsx b/apps/web-antd/src/components/tree/src/data.tsx new file mode 100644 index 0000000..2bbaf31 --- /dev/null +++ b/apps/web-antd/src/components/tree/src/data.tsx @@ -0,0 +1,85 @@ +import type { VxeGridProps } from '#/adapter/vxe-table'; +import type { ID } from '#/api/common'; +import type { MenuOption } from '#/api/system/menu/model'; + +import { h, markRaw } from 'vue'; + +import { FolderIcon, MenuIcon, OkButtonIcon, VbenIcon } from '@vben/icons'; + +export interface Permission { + checked: boolean; + id: ID; + label: string; +} + +export interface MenuPermissionOption extends MenuOption { + permissions: Permission[]; +} + +const menuTypes = { + C: { icon: markRaw(MenuIcon), value: '菜单' }, + F: { icon: markRaw(OkButtonIcon), value: '按钮' }, + M: { icon: markRaw(FolderIcon), value: '目录' }, +}; + +export const nodeOptions = [ + { label: '节点关联', value: true }, + { label: '节点独立', value: false }, +]; + +export const columns: VxeGridProps['columns'] = [ + { + type: 'checkbox', + title: '菜单名称', + field: 'label', + treeNode: true, + headerAlign: 'left', + align: 'left', + width: 230, + }, + { + title: '图标', + field: 'icon', + width: 80, + slots: { + default: ({ row }) => { + if (row?.icon === '#') { + return ''; + } + return ( + + + + ); + }, + }, + }, + { + title: '类型', + field: 'menuType', + width: 80, + slots: { + default: ({ row }) => { + const current = menuTypes[row.menuType as 'C' | 'F' | 'M']; + if (!current) { + return '未知'; + } + return ( + + {h(current.icon, { class: 'size-[18px]' })} + {current.value} + + ); + }, + }, + }, + { + title: '权限标识', + field: 'permissions', + headerAlign: 'left', + align: 'left', + slots: { + default: 'permissions', + }, + }, +]; diff --git a/apps/web-antd/src/components/tree/src/helper.tsx b/apps/web-antd/src/components/tree/src/helper.tsx new file mode 100644 index 0000000..9ce64a2 --- /dev/null +++ b/apps/web-antd/src/components/tree/src/helper.tsx @@ -0,0 +1,133 @@ +import type { MenuPermissionOption } from './data'; + +import type { useVbenVxeGrid } from '#/adapter/vxe-table'; +import type { MenuOption } from '#/api/system/menu/model'; + +import { eachTree, treeToList } from '@vben/utils'; + +import { difference, isEmpty, isUndefined } from 'lodash-es'; + +/** + * 权限列设置是否全选 + * @param record 行记录 + * @param checked 是否选中 + */ +export function setPermissionsChecked( + record: MenuPermissionOption, + checked: boolean, +) { + if (record?.permissions?.length > 0) { + // 全部设置为选中 + record.permissions.forEach((permission) => { + permission.checked = checked; + }); + } +} + +/** + * 设置当前行 & 所有子节点选中状态 + * @param record 行 + * @param checked 是否选中 + */ +export function rowAndChildrenChecked( + record: MenuPermissionOption, + checked: boolean, +) { + // 当前行选中 + setPermissionsChecked(record, checked); + // 所有子节点选中 + record?.children?.forEach?.((permission) => { + rowAndChildrenChecked(permission as MenuPermissionOption, checked); + }); +} + +/** + * void方法 会直接修改原始数据 + * 将树结构转为 tree+permissions结构 + * @param menus 后台返回的menu + */ +export function menusWithPermissions(menus: MenuOption[]) { + eachTree(menus, (item: MenuPermissionOption) => { + if (item.children && item.children.length > 0) { + /** + * 所有为按钮的节点提取出来 + * 需要注意 这里需要过滤目录下直接是按钮的情况item.menuType !== 'M' + * 将按钮往children添加而非加到permissions + */ + const permissions = item.children.filter( + (child: MenuOption) => child.menuType === 'F' && item.menuType !== 'M', + ); + // 取差集 + const diffCollection = difference(item.children, permissions); + // 更新后的children 即去除按钮 + item.children = diffCollection; + + // permissions作为字段添加到item + const permissionsArr = permissions.map((permission) => { + return { + id: permission.id, + label: permission.label, + checked: false, + }; + }); + item.permissions = permissionsArr; + } + }); +} + +/** + * 设置表格选中 + * @param checkedKeys 选中的keys + * @param menus 菜单 转换后的菜单 + * @param tableApi api + * @param association 是否节点关联 + */ +export function setTableChecked( + checkedKeys: (number | string)[], + menus: MenuPermissionOption[], + tableApi: ReturnType['1'], + association: boolean, +) { + // tree转list + const menuList: MenuPermissionOption[] = treeToList(menus); + // 拿到勾选的行数据 + let checkedRows = menuList.filter((item) => checkedKeys.includes(item.id)); + + /** + * 节点独立切换到节点关联 只需要最末尾的数据 即children为空 + */ + if (!association) { + checkedRows = checkedRows.filter( + (item) => isUndefined(item.children) || isEmpty(item.children), + ); + } + + // 设置行选中 & permissions选中 + checkedRows.forEach((item) => { + tableApi.grid.setCheckboxRow(item, true); + if (item?.permissions?.length > 0) { + item.permissions.forEach((permission) => { + if (checkedKeys.includes(permission.id)) { + permission.checked = true; + } + }); + } + }); + + /** + * 节点独立切换到节点关联 + * 勾选后还需要过滤权限没有任何勾选的情况 这时候取消行的勾选 + */ + if (!association) { + const emptyRows = checkedRows.filter((item) => { + if (isUndefined(item.permissions) || isEmpty(item.permissions)) { + return false; + } + return item.permissions.every( + (permission) => permission.checked === false, + ); + }); + // 设置为不选中 + tableApi.grid.setCheckboxRow(emptyRows, false); + } +} diff --git a/apps/web-antd/src/components/tree/src/hook.tsx b/apps/web-antd/src/components/tree/src/hook.tsx new file mode 100644 index 0000000..b7a70ad --- /dev/null +++ b/apps/web-antd/src/components/tree/src/hook.tsx @@ -0,0 +1,62 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import type { TourProps } from 'ant-design-vue'; + +import { defineComponent, ref } from 'vue'; + +import { useLocalStorage } from '@vueuse/core'; +import { Tour } from 'ant-design-vue'; + +/** + * 全屏引导 + * @returns value + */ +export function useFullScreenGuide() { + const open = ref(false); + /** + * 是否已读 只显示一次 + */ + const read = useLocalStorage('menu_select_fullscreen_read', false); + + function openGuide() { + if (!read.value) { + open.value = true; + } + } + + function closeGuide() { + open.value = false; + read.value = true; + } + + const steps: TourProps['steps'] = [ + { + title: '提示', + description: '点击这里可以全屏', + target: () => + document.querySelector( + 'div#menu-select-table .vxe-tools--operate > button[title="全屏"]', + )!, + }, + ]; + + const FullScreenGuide = defineComponent({ + name: 'FullScreenGuide', + inheritAttrs: false, + setup() { + return () => ( + + ); + }, + }); + + return { + FullScreenGuide, + openGuide, + closeGuide, + }; +} diff --git a/apps/web-antd/src/components/tree/src/menu-select-table.vue b/apps/web-antd/src/components/tree/src/menu-select-table.vue new file mode 100644 index 0000000..cccf08d --- /dev/null +++ b/apps/web-antd/src/components/tree/src/menu-select-table.vue @@ -0,0 +1,408 @@ + + + + + + diff --git a/apps/web-antd/src/components/tree/src/tree-select-panel.vue b/apps/web-antd/src/components/tree/src/tree-select-panel.vue new file mode 100644 index 0000000..e7597f9 --- /dev/null +++ b/apps/web-antd/src/components/tree/src/tree-select-panel.vue @@ -0,0 +1,221 @@ + + + diff --git a/apps/web-antd/src/components/upload/index.ts b/apps/web-antd/src/components/upload/index.ts new file mode 100644 index 0000000..aa5e2bc --- /dev/null +++ b/apps/web-antd/src/components/upload/index.ts @@ -0,0 +1,10 @@ +/* + * @Author: Mm + * @Date: 2025-05-06 14:08:27 + * @LastEditors: Mm + * @LastEditTime: 2025-05-22 17:07:33 + * 不写bug的程序员不是一个好程序员! + */ +export { default as FileUpload } from './src/file-upload.vue'; +export { default as FileTzUpload } from './src/file-tz-upload.vue'; +export { default as ImageUpload } from './src/image-upload.vue'; diff --git a/apps/web-antd/src/components/upload/src/file-tz-upload.vue b/apps/web-antd/src/components/upload/src/file-tz-upload.vue new file mode 100644 index 0000000..6e6cb93 --- /dev/null +++ b/apps/web-antd/src/components/upload/src/file-tz-upload.vue @@ -0,0 +1,280 @@ + + + + + + diff --git a/apps/web-antd/src/components/upload/src/file-upload.vue b/apps/web-antd/src/components/upload/src/file-upload.vue new file mode 100644 index 0000000..e646cbb --- /dev/null +++ b/apps/web-antd/src/components/upload/src/file-upload.vue @@ -0,0 +1,257 @@ + + + + + + diff --git a/apps/web-antd/src/components/upload/src/helper.ts b/apps/web-antd/src/components/upload/src/helper.ts new file mode 100644 index 0000000..ff70b41 --- /dev/null +++ b/apps/web-antd/src/components/upload/src/helper.ts @@ -0,0 +1,51 @@ +import { fileTypeFromBlob } from '@vben/utils'; + +/** + * 不支持txt文件 @see https://github.com/sindresorhus/file-type/issues/55 + * 需要自行修改 + * @param file file对象 + * @param accepts 文件类型数组 包括拓展名(不带点) 文件头(image/png等 不包括泛写法即image/*) + * @returns 是否通过文件类型校验 + */ +export async function checkFileType(file: File, accepts: string[]) { + if (!accepts || accepts?.length === 0) { + return true; + } + console.log(file); + const fileType = await fileTypeFromBlob(file); + if (!fileType) { + console.error('无法获取文件类型'); + return false; + } + console.log('文件类型', fileType); + // 是否文件拓展名/文件头任意有一个匹配 + return accepts.includes(fileType.ext) || accepts.includes(fileType.mime); +} + +/** + * 默认图片类型 + */ +export const defaultImageAccept = ['jpg', 'jpeg', 'png', 'gif', 'webp']; +/** + * 判断文件类型是否符合要求 + * @param file file对象 + * @param accepts 文件类型数组 包括拓展名(不带点) 文件头(image/png等 不包括泛写法即image/*) + * @returns 是否通过文件类型校验 + */ +export async function checkImageFileType(file: File, accepts: string[]) { + // 空的accepts 使用默认规则 + if (!accepts || accepts.length === 0) { + accepts = defaultImageAccept; + } + const fileType = await fileTypeFromBlob(file); + if (!fileType) { + console.error('无法获取文件类型'); + return false; + } + console.log('文件类型', fileType); + // 是否文件拓展名/文件头任意有一个匹配 + if (accepts.includes(fileType.ext) || accepts.includes(fileType.mime)) { + return true; + } + return false; +} diff --git a/apps/web-antd/src/components/upload/src/image-upload.vue b/apps/web-antd/src/components/upload/src/image-upload.vue new file mode 100644 index 0000000..40b7748 --- /dev/null +++ b/apps/web-antd/src/components/upload/src/image-upload.vue @@ -0,0 +1,333 @@ + + + + + + diff --git a/apps/web-antd/src/components/upload/src/typing.ts b/apps/web-antd/src/components/upload/src/typing.ts new file mode 100644 index 0000000..8d728b6 --- /dev/null +++ b/apps/web-antd/src/components/upload/src/typing.ts @@ -0,0 +1,37 @@ +import type { Recordable } from '@vben/types'; + +export enum UploadResultStatus { + DONE = 'done', + ERROR = 'error', + SUCCESS = 'success', + UPLOADING = 'uploading', +} + +export interface FileItem { + thumbUrl?: string; + name: string; + size: number | string; + type?: string; + percent: number; + file: File; + status?: UploadResultStatus; + response?: Recordable | { fileName: string; ossId: string; url: string }; + uuid: string; +} + +export interface Wrapper { + record: FileItem; + uidKey: string; + valueKey: string; +} + +export interface BaseFileItem { + uid: number | string; + url: string; + name?: string; +} +export interface PreviewFileItem { + url: string; + name: string; + type: string; +} diff --git a/apps/web-antd/src/components/upload/src/use-upload.ts b/apps/web-antd/src/components/upload/src/use-upload.ts new file mode 100644 index 0000000..4ae552f --- /dev/null +++ b/apps/web-antd/src/components/upload/src/use-upload.ts @@ -0,0 +1,61 @@ +import type { Ref } from 'vue'; + +import { computed, unref } from 'vue'; + +import { $t } from '@vben/locales'; + +export function useUploadType({ + acceptRef, + helpTextRef, + maxNumberRef, + maxSizeRef, +}: { + acceptRef: Ref; + helpTextRef: Ref; + maxNumberRef: Ref; + maxSizeRef: Ref; +}) { + // 文件类型限制 + const getAccept = computed(() => { + const accept = unref(acceptRef); + if (accept && accept.length > 0) { + return accept; + } + return []; + }); + const getStringAccept = computed(() => { + return unref(getAccept) + .map((item) => { + return item.indexOf('/') > 0 || item.startsWith('.') + ? item + : `.${item}`; + }) + .join(','); + }); + + // 支持jpg、jpeg、png格式,不超过2M,最多可选择10张图片,。 + const getHelpText = computed(() => { + const helpText = unref(helpTextRef); + if (helpText) { + return helpText; + } + const helpTexts: string[] = []; + + const accept = unref(acceptRef); + if (accept.length > 0) { + helpTexts.push($t('component.upload.accept', [accept.join(',')])); + } + + const maxSize = unref(maxSizeRef); + if (maxSize) { + helpTexts.push($t('component.upload.maxSize', [maxSize])); + } + + const maxNumber = unref(maxNumberRef); + if (maxNumber && maxNumber !== Infinity) { + helpTexts.push($t('component.upload.maxNumber', [maxNumber])); + } + return helpTexts.join(','); + }); + return { getAccept, getStringAccept, getHelpText }; +} diff --git a/apps/web-antd/src/custom.d.ts b/apps/web-antd/src/custom.d.ts new file mode 100644 index 0000000..b8a1bb9 --- /dev/null +++ b/apps/web-antd/src/custom.d.ts @@ -0,0 +1,13 @@ +/* + * @Author: Mm + * @Date: 2025-05-07 15:55:10 + * @LastEditors: Mm + * @LastEditTime: 2025-05-07 15:55:18 + * 不写bug的程序员不是一个好程序员! + */ +declare module '*.vue' { + import Vue from 'vue'; + export default Vue; +} + +declare module '*.js' diff --git a/apps/web-antd/src/layouts/auth.vue b/apps/web-antd/src/layouts/auth.vue new file mode 100644 index 0000000..18d415b --- /dev/null +++ b/apps/web-antd/src/layouts/auth.vue @@ -0,0 +1,23 @@ + + + diff --git a/apps/web-antd/src/layouts/basic.vue b/apps/web-antd/src/layouts/basic.vue new file mode 100644 index 0000000..3a8ea9a --- /dev/null +++ b/apps/web-antd/src/layouts/basic.vue @@ -0,0 +1,232 @@ + + + + + + diff --git a/apps/web-antd/src/layouts/index.ts b/apps/web-antd/src/layouts/index.ts new file mode 100644 index 0000000..a432078 --- /dev/null +++ b/apps/web-antd/src/layouts/index.ts @@ -0,0 +1,6 @@ +const BasicLayout = () => import('./basic.vue'); +const AuthPageLayout = () => import('./auth.vue'); + +const IFrameView = () => import('@vben/layouts').then((m) => m.IFrameView); + +export { AuthPageLayout, BasicLayout, IFrameView }; diff --git a/apps/web-antd/src/locales/README.md b/apps/web-antd/src/locales/README.md new file mode 100644 index 0000000..7b45103 --- /dev/null +++ b/apps/web-antd/src/locales/README.md @@ -0,0 +1,3 @@ +# locale + +每个app使用的国际化可能不同,这里用于扩展国际化的功能,例如扩展 dayjs、antd组件库的多语言切换,以及app本身的国际化文件。 diff --git a/apps/web-antd/src/locales/index.ts b/apps/web-antd/src/locales/index.ts new file mode 100644 index 0000000..5c0d650 --- /dev/null +++ b/apps/web-antd/src/locales/index.ts @@ -0,0 +1,103 @@ +import type { Locale } from 'ant-design-vue/es/locale'; + +import type { App } from 'vue'; + +import type { LocaleSetupOptions, SupportedLanguagesType } from '@vben/locales'; + +import { ref } from 'vue'; + +import { + $t, + setupI18n as coreSetup, + loadLocalesMapFromDir, +} from '@vben/locales'; +import { preferences } from '@vben/preferences'; + +import antdEnLocale from 'ant-design-vue/es/locale/en_US'; +import antdDefaultLocale from 'ant-design-vue/es/locale/zh_CN'; +import dayjs from 'dayjs'; + +const antdLocale = ref(antdDefaultLocale); + +const modules = import.meta.glob('./langs/**/*.json'); + +const localesMap = loadLocalesMapFromDir( + /\.\/langs\/([^/]+)\/(.*)\.json$/, + modules, +); +/** + * 加载应用特有的语言包 + * 这里也可以改造为从服务端获取翻译数据 + * @param lang + */ +async function loadMessages(lang: SupportedLanguagesType) { + const [appLocaleMessages] = await Promise.all([ + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + localesMap[lang]!(), + loadThirdPartyMessage(lang), + ]); + return appLocaleMessages.default; +} + +/** + * 加载第三方组件库的语言包 + * @param lang + */ +async function loadThirdPartyMessage(lang: SupportedLanguagesType) { + await Promise.all([loadAntdLocale(lang), loadDayjsLocale(lang)]); +} + +/** + * 加载dayjs的语言包 + * @param lang + */ +async function loadDayjsLocale(lang: SupportedLanguagesType) { + let locale; + switch (lang) { + case 'en-US': { + locale = await import('dayjs/locale/en'); + break; + } + case 'zh-CN': { + locale = await import('dayjs/locale/zh-cn'); + break; + } + // 默认使用英语 + default: { + locale = await import('dayjs/locale/en'); + } + } + if (locale) { + dayjs.locale(locale); + } else { + console.error(`Failed to load dayjs locale for ${lang}`); + } +} + +/** + * 加载antd的语言包 + * @param lang + */ +async function loadAntdLocale(lang: SupportedLanguagesType) { + switch (lang) { + case 'en-US': { + antdLocale.value = antdEnLocale; + break; + } + case 'zh-CN': { + antdLocale.value = antdDefaultLocale; + break; + } + } +} + +async function setupI18n(app: App, options: LocaleSetupOptions = {}) { + await coreSetup(app, { + defaultLocale: preferences.app.locale, + loadMessages, + missingWarn: !import.meta.env.PROD, + ...options, + }); +} + +export { $t, antdLocale, setupI18n }; diff --git a/apps/web-antd/src/locales/langs/en-US/component.json b/apps/web-antd/src/locales/langs/en-US/component.json new file mode 100644 index 0000000..c8ebf68 --- /dev/null +++ b/apps/web-antd/src/locales/langs/en-US/component.json @@ -0,0 +1,55 @@ +{ + "cropper": { + "selectImage": "Select Image", + "uploadSuccess": "Uploaded success!", + "imageTooBig": "Image too big", + "modalTitle": "Avatar upload", + "okText": "Confirm and upload", + "btn_reset": "Reset", + "btn_rotate_left": "Counterclockwise rotation", + "btn_rotate_right": "Clockwise rotation", + "btn_scale_x": "Flip horizontal", + "btn_scale_y": "Flip vertical", + "btn_zoom_in": "Zoom in", + "btn_zoom_out": "Zoom out", + "preview": "Preview" + }, + "tenantToggle": { + "placeholder": "Please select a tenant", + "switch": "Switch to tenant: ", + "reset": "Reset to default tenant" + }, + "notice": { + "title": "Notice", + "received": "You have received a new message" + }, + "upload": { + "save": "Save", + "upload": "Upload", + "imgUpload": "ImageUpload", + "uploaded": "Uploaded", + "operating": "Operating", + "del": "Delete", + "download": "download", + "saveWarn": "Please wait for the file to upload and save!", + "saveError": "There is no file successfully uploaded and cannot be saved!", + "preview": "Preview", + "choose": "Select the file", + "accept": "Support {0} format", + "acceptUpload": "Only upload files in {0} format", + "maxSize": "A single file does not exceed {0}MB ", + "maxSizeMultiple": "Only upload files up to {0}MB!", + "maxNumber": "Only upload up to {0} files", + "legend": "Legend", + "fileName": "File name", + "fileSize": "File size", + "fileStatue": "File status", + "pending": "Pending", + "startUpload": "Start upload", + "uploadSuccess": "Upload successfully", + "uploadError": "Upload failed", + "uploading": "Uploading", + "uploadWait": "Please wait for the file upload to finish", + "reUploadFailed": "Re-upload failed files" + } +} diff --git a/apps/web-antd/src/locales/langs/en-US/demos.json b/apps/web-antd/src/locales/langs/en-US/demos.json new file mode 100644 index 0000000..0715643 --- /dev/null +++ b/apps/web-antd/src/locales/langs/en-US/demos.json @@ -0,0 +1,12 @@ +{ + "title": "Demos", + "antd": "Ant Design Vue", + "vben": { + "title": "Project", + "about": "About", + "document": "Document", + "antdv": "Ant Design Vue Version", + "naive-ui": "Naive UI Version", + "element-plus": "Element Plus Version" + } +} diff --git a/apps/web-antd/src/locales/langs/en-US/http.json b/apps/web-antd/src/locales/langs/en-US/http.json new file mode 100644 index 0000000..c3ef2a0 --- /dev/null +++ b/apps/web-antd/src/locales/langs/en-US/http.json @@ -0,0 +1,7 @@ +{ + "apiRequestFailed": "Operation failed", + "operationSuccess": "Operation Success", + "successTip": "Success Tip", + "errorTip": "Error Tip", + "loginTimeout": "Login timeout, please log in again" +} diff --git a/apps/web-antd/src/locales/langs/en-US/menu.json b/apps/web-antd/src/locales/langs/en-US/menu.json new file mode 100644 index 0000000..ac93f71 --- /dev/null +++ b/apps/web-antd/src/locales/langs/en-US/menu.json @@ -0,0 +1,55 @@ +{ + "root": "Root", + "system": { + "root": "System", + "user": "User", + "role": "Role", + "menu": "Menu", + "dept": "Department", + "post": "Post", + "dict": "Dictionary", + "config": "Parameter Settings", + "notice": "Notifications", + "log": { + "root": "Log", + "operation": "Operation Log", + "login": "Login Log" + }, + "oss": "File", + "client": "Client" + }, + "tenant": { + "root": "Tenant", + "package": "Package" + }, + "monitor": { + "root": "System Monitoring", + "online": "Online Users", + "cache": "Cache Monitoring", + "admin": "Admin Monitoring", + "job": "Task Scheduling Center" + }, + "tool": { + "root": "System Tools", + "gen": "Code Generation" + }, + "workflow": { + "root": "Workflow", + "category": "Process Category", + "model": "Model", + "define": "Process Definition", + "monitor": { + "root": "Process Monitoring", + "instance": "Process Instance", + "todo": "Pending Tasks" + }, + "form": "Form" + }, + "task": { + "root": "My Tasks", + "apply": "My Initiated Tasks", + "todo": "My Pending Tasks", + "done": "My Completed Tasks", + "cc": "My CC" + } +} diff --git a/apps/web-antd/src/locales/langs/en-US/page.json b/apps/web-antd/src/locales/langs/en-US/page.json new file mode 100644 index 0000000..8fe0f0c --- /dev/null +++ b/apps/web-antd/src/locales/langs/en-US/page.json @@ -0,0 +1,15 @@ +{ + "auth": { + "login": "Login", + "register": "Register", + "codeLogin": "Code Login", + "qrcodeLogin": "Qr Code Login", + "forgetPassword": "Forget Password", + "oauthLogin": "Oauth Login" + }, + "dashboard": { + "title": "Dashboard", + "analytics": "Analytics", + "workspace": "Workspace" + } +} diff --git a/apps/web-antd/src/locales/langs/en-US/pages.json b/apps/web-antd/src/locales/langs/en-US/pages.json new file mode 100644 index 0000000..ea66e51 --- /dev/null +++ b/apps/web-antd/src/locales/langs/en-US/pages.json @@ -0,0 +1,23 @@ +{ + "common": { + "add": "Add", + "edit": "Edit", + "delete": "Delete", + "more": "More", + "search": "Search", + "reset": "Reset", + "import": "Import", + "export": "Export", + "expand": "Expand", + "collapse": "Collapse", + "info": "Info", + "clear": "Clear", + "unlock": "Unlock", + "download": "Download", + "sync": "Sync", + "refresh": "Refresh", + "generate": "Generate", + "downloadLoading": "Downloading... Please wait.", + "preview": "Preview" + } +} diff --git a/apps/web-antd/src/locales/langs/zh-CN/component.json b/apps/web-antd/src/locales/langs/zh-CN/component.json new file mode 100644 index 0000000..b0ef5a2 --- /dev/null +++ b/apps/web-antd/src/locales/langs/zh-CN/component.json @@ -0,0 +1,55 @@ +{ + "cropper": { + "selectImage": "选择图片", + "uploadSuccess": "上传成功", + "imageTooBig": "图片超限", + "modalTitle": "头像上传", + "okText": "确认并上传", + "btn_reset": "重置", + "btn_rotate_left": "逆时针旋转", + "btn_rotate_right": "顺时针旋转", + "btn_scale_x": "水平翻转", + "btn_scale_y": "垂直翻转", + "btn_zoom_in": "放大", + "btn_zoom_out": "缩小", + "preview": "预览" + }, + "tenantToggle": { + "placeholder": "选择租户", + "switch": "切换当前租户为: ", + "reset": "还原为默认租户" + }, + "notice": { + "title": "消息", + "received": "收到新消息" + }, + "upload": { + "save": "保存", + "upload": "上传", + "imgUpload": "图片上传", + "uploaded": "已上传", + "operating": "操作", + "del": "删除", + "download": "下载", + "saveWarn": "请等待文件上传后,保存!", + "saveError": "没有上传成功的文件,无法保存!", + "preview": "预览", + "choose": "选择文件", + "accept": "支持{0}格式", + "acceptUpload": "只能上传{0}格式文件", + "maxSize": "单个文件不超过{0}MB", + "maxSizeMultiple": "只能上传不超过{0}MB的文件!", + "maxNumber": "最多只能上传{0}个文件", + "legend": "略缩图", + "fileName": "文件名", + "fileSize": "文件大小", + "fileStatue": "状态", + "pending": "待上传", + "startUpload": "开始上传", + "uploadSuccess": "上传成功", + "uploadError": "上传失败", + "uploading": "上传中", + "uploadWait": "请等待文件上传结束后操作", + "reUploadFailed": "重新上传失败文件" + } +} diff --git a/apps/web-antd/src/locales/langs/zh-CN/demos.json b/apps/web-antd/src/locales/langs/zh-CN/demos.json new file mode 100644 index 0000000..93ee722 --- /dev/null +++ b/apps/web-antd/src/locales/langs/zh-CN/demos.json @@ -0,0 +1,12 @@ +{ + "title": "演示", + "antd": "Ant Design Vue", + "vben": { + "title": "项目", + "about": "关于", + "document": "文档", + "antdv": "Ant Design Vue 版本", + "naive-ui": "Naive UI 版本", + "element-plus": "Element Plus 版本" + } +} diff --git a/apps/web-antd/src/locales/langs/zh-CN/http.json b/apps/web-antd/src/locales/langs/zh-CN/http.json new file mode 100644 index 0000000..1df058f --- /dev/null +++ b/apps/web-antd/src/locales/langs/zh-CN/http.json @@ -0,0 +1,7 @@ +{ + "apiRequestFailed": "请求出错,请稍候重试", + "operationSuccess": "操作成功", + "successTip": "成功提示", + "errorTip": "错误提示", + "loginTimeout": "登录超时, 请重新登录" +} diff --git a/apps/web-antd/src/locales/langs/zh-CN/menu.json b/apps/web-antd/src/locales/langs/zh-CN/menu.json new file mode 100644 index 0000000..6772532 --- /dev/null +++ b/apps/web-antd/src/locales/langs/zh-CN/menu.json @@ -0,0 +1,55 @@ +{ + "root": "根目录", + "system": { + "root": "系统管理", + "user": "用户管理", + "role": "角色管理", + "menu": "菜单管理", + "dept": "部门管理", + "post": "岗位管理", + "dict": "字典管理", + "config": "参数设置", + "notice": "通知公告", + "log": { + "root": "日志管理", + "operation": "操作日志", + "login": "登录日志" + }, + "oss": "文件管理", + "client": "客户端管理" + }, + "tenant": { + "root": "租户管理", + "package": "套餐管理" + }, + "monitor": { + "root": "系统监控", + "online": "在线用户", + "cache": "缓存监控", + "admin": "Admin监控", + "job": "任务调度中心" + }, + "tool": { + "root": "系统工具", + "gen": "代码生成" + }, + "workflow": { + "root": "工作流", + "category": "流程分类", + "model": "模型管理", + "define": "流程定义", + "monitor": { + "root": "流程监控", + "instance": "流程实例", + "todo": "待办任务" + }, + "form": "表单管理" + }, + "task": { + "root": "我的任务", + "apply": "我发起的", + "todo": "我的待办", + "done": "我的已办", + "cc": "我的抄送" + } +} diff --git a/apps/web-antd/src/locales/langs/zh-CN/page.json b/apps/web-antd/src/locales/langs/zh-CN/page.json new file mode 100644 index 0000000..8a33b9f --- /dev/null +++ b/apps/web-antd/src/locales/langs/zh-CN/page.json @@ -0,0 +1,15 @@ +{ + "auth": { + "login": "登录", + "register": "注册", + "codeLogin": "验证码登录", + "qrcodeLogin": "二维码登录", + "forgetPassword": "忘记密码", + "oauthLogin": "第三方登录" + }, + "dashboard": { + "title": "概览", + "analytics": "统计概览", + "workspace": "工作台" + } +} diff --git a/apps/web-antd/src/locales/langs/zh-CN/pages.json b/apps/web-antd/src/locales/langs/zh-CN/pages.json new file mode 100644 index 0000000..1bff1bb --- /dev/null +++ b/apps/web-antd/src/locales/langs/zh-CN/pages.json @@ -0,0 +1,23 @@ +{ + "common": { + "add": "新增", + "edit": "编辑", + "delete": "删除", + "more": "更多", + "search": "搜索", + "reset": "重置", + "import": "导入", + "export": "导出", + "expand": "展开", + "collapse": "收起", + "info": "详情", + "clear": "清空", + "unlock": "解锁", + "download": "下载", + "sync": "同步", + "refresh": "刷新", + "generate": "生成", + "downloadLoading": "下载中, 请稍后...", + "preview": "预览" + } +} diff --git a/apps/web-antd/src/main.ts b/apps/web-antd/src/main.ts new file mode 100644 index 0000000..fdf4fc3 --- /dev/null +++ b/apps/web-antd/src/main.ts @@ -0,0 +1,38 @@ +/* + * @Author: Mm + * @Date: 2025-05-06 14:08:27 + * @LastEditors: Mm + * @LastEditTime: 2025-05-26 10:34:53 + * 不写bug的程序员不是一个好程序员! + */ +import { initPreferences } from '@vben/preferences'; +import { unmountGlobalLoading } from '@vben/utils'; + +import { overridesPreferences } from './preferences'; + +/** + * 应用初始化完成之后再进行页面加载渲染 + */ +async function initApplication() { + // name用于指定项目唯一标识 + // 用于区分不同项目的偏好设置以及存储数据的key前缀以及其他一些需要隔离的数据 + const env = import.meta.env.PROD ? 'prod' : 'dev'; + const appVersion = import.meta.env.VITE_APP_VERSION; + const namespace = `${import.meta.env.VITE_APP_NAMESPACE}-${appVersion}-${env}`; + + // app偏好设置初始化 + await initPreferences({ + namespace, + overrides: overridesPreferences, + }); + + // 启动应用并挂载 + // vue应用主要逻辑及视图 + const { bootstrap } = await import('./bootstrap'); + await bootstrap(namespace); + + // 移除并销毁loading + unmountGlobalLoading(); +} + +initApplication(); diff --git a/apps/web-antd/src/preferences.ts b/apps/web-antd/src/preferences.ts new file mode 100644 index 0000000..0b9708d --- /dev/null +++ b/apps/web-antd/src/preferences.ts @@ -0,0 +1,72 @@ +/* + * @Author: Mm + * @Date: 2025-05-06 14:08:27 + * @LastEditors: Mm + * @LastEditTime: 2025-05-13 11:16:04 + * 不写bug的程序员不是一个好程序员! + */ +import { defineOverridesPreferences } from '@vben/preferences'; + +/** + * @description 项目配置文件 + * 只需要覆盖项目中的一部分配置,不需要的配置不用覆盖,会自动使用默认配置 + * !!! 更改配置后请清空缓存,否则可能不生效 + */ +export const overridesPreferences = defineOverridesPreferences({ + // overrides + app: { + layout: "header-sidebar-nav",//侧边栏导航 + /** + * 不要动这里 后端路由模式 + */ + accessMode: 'backend', + /** + * 不需要refresh token 由后端处理 + */ + enableRefreshToken: false, + /** + * 这里可以设置默认头像 url链接或vite导入的图片链接 + */ + // defaultAvatar: '', + name: import.meta.env.VITE_APP_TITLE, + /** + * 不支持modal模式 需要改动的地方太多 + * 1. 正常重新登录后不会再触发接口请求 即触发登录超时的页面为空数据 + * 2. 切换租户登录后不会重新加载菜单 + */ + // loginExpiredMode: 'modal', + }, + footer: { + /** + * 不显示footer + */ + enable: false, + }, + tabbar: { + /** + * 标签tab 持久化 关闭 + */ + persist: false, + // styleType: 'card', + }, + theme: { + /** + * 浅色sidebar + */ + semiDarkSidebar: false, + }, + /** + * !!! 更改配置后请清空浏览器缓存 + * 在这里更换logo + * source可选值: + * 1. 本地public目录下的图片 需要加上/ 比如:/logo.png + * 2. 网络图片链接 + * 3. vite导入的图片 import xxx from 'xxx.png' + * + * !!! 更改配置后请清空浏览器缓存 + */ + logo: { + enable: true, + source: '/logo.png', + }, +}); diff --git a/apps/web-antd/src/router/access.ts b/apps/web-antd/src/router/access.ts new file mode 100644 index 0000000..5283f2a --- /dev/null +++ b/apps/web-antd/src/router/access.ts @@ -0,0 +1,223 @@ +import type { + ComponentRecordType, + GenerateMenuAndRoutesOptions, + RouteRecordStringComponent, +} from '@vben/types'; + +import type { Menu } from '#/api'; + +import { generateAccessible } from '@vben/access'; +import { preferences } from '@vben/preferences'; + +import { message } from 'ant-design-vue'; +import { cloneDeep } from 'lodash-es'; + +import { getAllMenusApi } from '#/api'; +import { BasicLayout, IFrameView } from '#/layouts'; +import { $t } from '#/locales'; + +import { localMenuList } from './routes/local'; + +const forbiddenComponent = () => import('#/views/_core/fallback/forbidden.vue'); +const NotFoundComponent = () => import('#/views/_core/fallback/not-found.vue'); + +/** + * 后台路由转vben路由 + * @param menuList 后台菜单 + * @param parentPath 上级目录 + * @returns vben路由 + */ +function backMenuToVbenMenu( + menuList: Menu[], + parentPath = '', +): RouteRecordStringComponent[] { + const resultList: RouteRecordStringComponent[] = []; + menuList.forEach((menu) => { + // 根目录为菜单形式 + // 固定有一个children children为当前菜单 + if (menu.path === '/' && menu.children && menu.children.length === 1) { + if (!menu.children || !menu.children[0]) { + return; + } + + // 需要处理根目录为内嵌的情况 不会带InnerLink + if (/^https?:\/\//.test(menu.children[0].path)) { + menu.children[0].component = 'InnerLink'; + menu.children[0].path = menu.children[0].path + .replaceAll(/^https?:\/\//g, '') + .replaceAll('/#/', '') + .replaceAll('#', '') + .replaceAll(/[?&]/g, ''); + } + + // 取子路径作为父级路径 + const path = menu.children[0].path; + // 取子菜单的meta作为当前菜单的meta + menu.meta = menu.children[0].meta; + // 由于在一级路由 父级路径需要加上/ + menu.path = `/${path}`; + menu.component = 'RootMenu'; + // 将子路径设置为'' + menu.children[0].path = ''; + } + + // 外链: http开头 & 组件为Layout || ParentView + // 正则判断是否为http://或者https://开头 + if ( + /^https?:\/\//.test(menu.path) && + (menu.component === 'Layout' || menu.component === 'ParentView') + ) { + menu.component = 'Link'; + } + + // 内嵌iframe 组件为InnerLink + if (menu.meta?.link && menu.component === 'InnerLink') { + menu.component = 'IFrameView'; + } + + /** + * 拼接path + * menu.path为''(根目录路由) 则不拼接 + */ + if (parentPath && menu.path) { + menu.path = `${parentPath}/${menu.path}`; + } + + // 创建vben路由对象 + const vbenRoute: RouteRecordStringComponent = { + component: menu.component, + meta: { + // 当前路由不在菜单显示 但是可以通过链接访问 + // 不可访问的路由由后端控制隐藏(不返回对应路由) + hideInMenu: menu.hidden, + icon: menu.meta?.icon, + keepAlive: !menu.meta?.noCache, + title: menu.meta?.title, + }, + name: menu.name, + path: menu.path, + }; + + // 添加路由参数信息 + if (menu.query) { + try { + const query = JSON.parse(menu.query); + vbenRoute.meta && (vbenRoute.meta.query = query); + } catch { + console.error('错误的路由参数类型, 必须为[json]格式'); + } + } + + /** + * 处理不同组件 + */ + switch (menu.component) { + /** + * iframe内嵌 + */ + case 'IFrameView': { + vbenRoute.component = 'IFrameView'; + if (vbenRoute.meta) { + vbenRoute.meta.iframeSrc = menu.meta.link; + } + /** + * 需要判断特殊情况 比如vue的hash是带#的 + * 比如链接 aaa.com/#/bbb path会转换为 aaa/com/#/bbb + * 比如链接 aaa.com/?bbb=xxx + * 需要去除# 否则无法被添加到路由 + */ + vbenRoute.path = vbenRoute.path + // 替换https:// 或者 http:// + .replaceAll(/^https?:\/\//g, '') + .replaceAll('/#/', '') + .replaceAll('#', '') + .replaceAll(/[?&]/g, ''); + break; + } + case 'Layout': { + vbenRoute.component = 'BasicLayout'; + break; + } + /** + * 外链 新窗口打开 + */ + case 'Link': { + if (vbenRoute.meta) { + vbenRoute.meta.link = menu.meta.link; + } + vbenRoute.component = 'BasicLayout'; + break; + } + /** + * 三级以上菜单 父级component为ParentView + * 不能为layout 会套两层BasicLayout + */ + case 'ParentView': { + vbenRoute.component = ''; + break; + } + /** + * 根目录菜单 + */ + case 'RootMenu': { + if (vbenRoute.meta) { + vbenRoute.meta.hideChildrenInMenu = true; + } + vbenRoute.component = 'BasicLayout'; + break; + } + /** + * 其他自定义组件 如system/user/index 拼接/ + */ + default: { + vbenRoute.component = `/${menu.component}`; + break; + } + } + + // children处理 + if (menu.children && menu.children.length > 0) { + vbenRoute.children = backMenuToVbenMenu(menu.children, menu.path); + } + // 添加 + resultList.push(vbenRoute); + }); + return resultList; +} + +async function generateAccess(options: GenerateMenuAndRoutesOptions) { + const pageMap: ComponentRecordType = import.meta.glob('../views/**/*.vue'); + + const layoutMap: ComponentRecordType = { + BasicLayout, + IFrameView, + NotFoundComponent, + }; + + return await generateAccessible(preferences.app.accessMode, { + ...options, + fetchMenuListAsync: async () => { + // 清除以前的message + message.destroy(); + message.loading({ + content: `${$t('common.loadingMenu')}...`, + duration: 1, + }); + // 后台返回路由/菜单 + const backMenuList = await getAllMenusApi(); + // 转换为vben能用的路由 + const vbenMenuList = backMenuToVbenMenu(backMenuList); + // 特别注意 这里要深拷贝 + const menuList = [...cloneDeep(localMenuList), ...vbenMenuList]; + // console.log('menuList', menuList); + return menuList; + }, + // 可以指定没有权限跳转403页面 + forbiddenComponent, + // 如果 route.meta.menuVisibleWithForbidden = true + layoutMap, + pageMap, + }); +} + +export { generateAccess }; diff --git a/apps/web-antd/src/router/guard.ts b/apps/web-antd/src/router/guard.ts new file mode 100644 index 0000000..cbb5235 --- /dev/null +++ b/apps/web-antd/src/router/guard.ts @@ -0,0 +1,133 @@ +import type { Router } from 'vue-router'; + +import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants'; +import { preferences } from '@vben/preferences'; +import { useAccessStore, useUserStore } from '@vben/stores'; +import { startProgress, stopProgress } from '@vben/utils'; + +import { accessRoutes, coreRouteNames } from '#/router/routes'; +import { useAuthStore } from '#/store'; + +import { generateAccess } from './access'; + +/** + * 通用守卫配置 + * @param router + */ +function setupCommonGuard(router: Router) { + // 记录已经加载的页面 + const loadedPaths = new Set(); + + router.beforeEach(async (to) => { + to.meta.loaded = loadedPaths.has(to.path); + + // 页面加载进度条 + if (!to.meta.loaded && preferences.transition.progress) { + startProgress(); + } + return true; + }); + + router.afterEach((to) => { + // 记录页面是否加载,如果已经加载,后续的页面切换动画等效果不在重复执行 + + loadedPaths.add(to.path); + + // 关闭页面加载进度条 + if (preferences.transition.progress) { + stopProgress(); + } + }); +} + +/** + * 权限访问守卫配置 + * @param router + */ +function setupAccessGuard(router: Router) { + router.beforeEach(async (to, from) => { + const accessStore = useAccessStore(); + const userStore = useUserStore(); + const authStore = useAuthStore(); + + // 基本路由,这些路由不需要进入权限拦截 + if (coreRouteNames.includes(to.name as string)) { + if (to.path === LOGIN_PATH && accessStore.accessToken) { + return decodeURIComponent( + (to.query?.redirect as string) || + userStore.userInfo?.homePath || + DEFAULT_HOME_PATH, + ); + } + return true; + } + + // accessToken 检查 + if (!accessStore.accessToken) { + // 明确声明忽略权限访问权限,则可以访问 + if (to.meta.ignoreAccess) { + return true; + } + + // 没有访问权限,跳转登录页面 + if (to.fullPath !== LOGIN_PATH) { + return { + path: LOGIN_PATH, + // 如不需要,直接删除 query + query: + to.fullPath === DEFAULT_HOME_PATH + ? {} + : { redirect: encodeURIComponent(to.fullPath) }, + // 携带当前跳转的页面,登录后重新跳转该页面 + replace: true, + }; + } + return to; + } + + // 是否已经生成过动态路由 + if (accessStore.isAccessChecked) { + return true; + } + + // 生成路由表 + // 当前登录用户拥有的角色标识列表 + const userInfo = userStore.userInfo || (await authStore.fetchUserInfo()); + const userRoles = userInfo.roles ?? []; + + // 生成菜单和路由 + const { accessibleMenus, accessibleRoutes } = await generateAccess({ + roles: userRoles, + router, + // 则会在菜单中显示,但是访问会被重定向到403 + routes: accessRoutes, + }); + + // 保存菜单信息和路由信息 + accessStore.setAccessMenus(accessibleMenus); + accessStore.setAccessRoutes(accessibleRoutes); + accessStore.setIsAccessChecked(true); + const redirectPath = (from.query.redirect ?? + (to.path === DEFAULT_HOME_PATH + ? userInfo.homePath || DEFAULT_HOME_PATH + : to.fullPath)) as string; + + return { + ...router.resolve(decodeURIComponent(redirectPath)), + replace: true, + }; + }); +} + +/** + * 项目守卫配置 + * @param router + */ +function createRouterGuard(router: Router) { + /** 通用 */ + setupCommonGuard(router); + /** 权限访问 */ + setupAccessGuard(router); +} + +export { createRouterGuard }; diff --git a/apps/web-antd/src/router/index.ts b/apps/web-antd/src/router/index.ts new file mode 100644 index 0000000..4840230 --- /dev/null +++ b/apps/web-antd/src/router/index.ts @@ -0,0 +1,37 @@ +import { + createRouter, + createWebHashHistory, + createWebHistory, +} from 'vue-router'; + +import { resetStaticRoutes } from '@vben/utils'; + +import { createRouterGuard } from './guard'; +import { routes } from './routes'; + +/** + * @zh_CN 创建vue-router实例 + */ +const router = createRouter({ + history: + import.meta.env.VITE_ROUTER_HISTORY === 'hash' + ? createWebHashHistory(import.meta.env.VITE_BASE) + : createWebHistory(import.meta.env.VITE_BASE), + // 应该添加到路由的初始路由列表。 + routes, + scrollBehavior: (to, _from, savedPosition) => { + if (savedPosition) { + return savedPosition; + } + return to.hash ? { behavior: 'smooth', el: to.hash } : { left: 0, top: 0 }; + }, + // 是否应该禁止尾部斜杠。 + // strict: true, +}); + +const resetRoutes = () => resetStaticRoutes(router, routes); + +// 创建路由守卫 +createRouterGuard(router); + +export { resetRoutes, router }; diff --git a/apps/web-antd/src/router/routes/core.ts b/apps/web-antd/src/router/routes/core.ts new file mode 100644 index 0000000..79e4f8f --- /dev/null +++ b/apps/web-antd/src/router/routes/core.ts @@ -0,0 +1,113 @@ +/* + * @Author: Mm + * @Date: 2025-05-06 14:08:27 + * @LastEditors: Mm + * @LastEditTime: 2025-05-07 15:48:18 + * 不写bug的程序员不是一个好程序员! + */ +import type { RouteRecordRaw } from 'vue-router'; + +import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants'; + +import { AuthPageLayout, BasicLayout } from '#/layouts'; +import { $t } from '#/locales'; +import Login from '#/views/_core/authentication/login.vue'; + +/** 全局404页面 */ +const fallbackNotFoundRoute: RouteRecordRaw = { + component: () => import('#/views/_core/fallback/not-found.vue'), + meta: { + hideInBreadcrumb: true, + hideInMenu: true, + hideInTab: true, + title: '404', + }, + name: 'FallbackNotFound', + path: '/:path(.*)*', +}; + +/** 基本路由,这些路由是必须存在的 */ +const coreRoutes: RouteRecordRaw[] = [ + /** + * 根路由 + * 使用基础布局,作为所有页面的父级容器,子级就不必配置BasicLayout。 + * 此路由必须存在,且不应修改 + */ + { + component: BasicLayout, + meta: { + hideInBreadcrumb: true, + title: 'Root', + }, + name: 'Root', + path: '/', + redirect: DEFAULT_HOME_PATH, + children: [], + }, + { + component: () => import('#/views/_core/social-callback/index.vue'), + meta: { + title: $t('page.auth.oauthLogin'), + }, + name: 'OAuthRedirect', + path: '/social-callback', + }, + { + component: AuthPageLayout, + meta: { + hideInTab: true, + title: 'Authentication', + }, + name: 'Authentication', + path: '/auth', + redirect: LOGIN_PATH, + children: [ + { + name: 'Login', + path: 'login', + component: Login, + meta: { + title: $t('page.auth.login'), + }, + }, + { + name: 'CodeLogin', + path: 'code-login', + component: () => import('#/views/_core/authentication/code-login.vue'), + meta: { + title: $t('page.auth.codeLogin'), + }, + }, + { + name: 'QrCodeLogin', + path: 'qrcode-login', + component: () => + import('#/views/_core/authentication/qrcode-login.vue'), + meta: { + title: $t('page.auth.qrcodeLogin'), + }, + }, + { + name: 'ForgetPassword', + path: 'forget-password', + component: () => + import('#/views/_core/authentication/forget-password.vue'), + meta: { + title: $t('page.auth.forgetPassword'), + }, + }, + { + name: 'Register', + path: 'register', + component: () => import('#/views/_core/authentication/register.vue'), + meta: { + title: $t('page.auth.register'), + }, + }, + ], + }, + + +]; + +export { coreRoutes, fallbackNotFoundRoute }; diff --git a/apps/web-antd/src/router/routes/index.ts b/apps/web-antd/src/router/routes/index.ts new file mode 100644 index 0000000..1d8e5bb --- /dev/null +++ b/apps/web-antd/src/router/routes/index.ts @@ -0,0 +1,42 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { mergeRouteModules, traverseTreeValues } from '@vben/utils'; + +import { coreRoutes, fallbackNotFoundRoute } from './core'; + +const dynamicRouteFiles = import.meta.glob('./modules/**/*.ts', { + eager: true, +}); + +import {workflowRoutes} from './workflow' + +// 有需要可以自行打开注释,并创建文件夹 +// const externalRouteFiles = import.meta.glob('./external/**/*.ts', { eager: true }); +// const staticRouteFiles = import.meta.glob('./static/**/*.ts', { eager: true }); + +/** 动态路由 */ +const dynamicRoutes: RouteRecordRaw[] = mergeRouteModules(dynamicRouteFiles); + +/** 外部路由列表,访问这些页面可以不需要Layout,可能用于内嵌在别的系统(不会显示在菜单中) */ +// const externalRoutes: RouteRecordRaw[] = mergeRouteModules(externalRouteFiles); +// const staticRoutes: RouteRecordRaw[] = mergeRouteModules(staticRouteFiles); +const staticRoutes: RouteRecordRaw[] = []; +const externalRoutes: RouteRecordRaw[] = []; + +/** 路由列表,由基本路由、外部路由和404兜底路由组成 + * 无需走权限验证(会一直显示在菜单中) */ +const routes: RouteRecordRaw[] = [ + ...coreRoutes, + ...externalRoutes, + ...workflowRoutes, + fallbackNotFoundRoute, +]; + +/** 基本路由(登录, 第三方登录, 注册等) */ +const basicRoutes = [...coreRoutes]; +/** 基本路由列表,这些路由不需要进入权限拦截 */ +const coreRouteNames = traverseTreeValues(basicRoutes, (route) => route.name); + +/** 有权限校验的路由列表,包含动态路由和静态路由 */ +const accessRoutes = [...dynamicRoutes, ...staticRoutes]; +export { accessRoutes, coreRouteNames, routes }; diff --git a/apps/web-antd/src/router/routes/local.ts b/apps/web-antd/src/router/routes/local.ts new file mode 100644 index 0000000..349fe69 --- /dev/null +++ b/apps/web-antd/src/router/routes/local.ts @@ -0,0 +1,145 @@ +/* + * @Author: Mm + * @Date: 2025-05-06 14:08:27 + * @LastEditors: hcc + * @LastEditTime: 2025-06-07 09:59:11 + * 不写bug的程序员不是一个好程序员! + */ +import type { RouteRecordStringComponent } from '@vben/types'; + +import { $t } from '@vben/locales'; + +/** + * 该文件放非后台返回的路由 比如个人中心 等需要跳转显示的页面 + */ +const localRoutes: RouteRecordStringComponent[] = [ + { + component: '/_core/profile/index', + meta: { + icon: 'mingcute:profile-line', + title: $t('ui.widgets.profile'), + hideInMenu: true, + }, + name: 'Profile', + path: '/profile', + }, + { + component: '/system/oss-config/index', + meta: { + activePath: '/system/oss', + icon: 'ant-design:setting-outlined', + title: 'oss配置', + hideInMenu: true, + }, + name: 'OssConfig', + path: '/system/oss-config', + }, + { + component: '/tool/gen/edit-gen', + meta: { + activePath: '/tool/gen', + icon: 'tabler:code', + title: '生成配置', + hideInMenu: true, + }, + name: 'GenConfig', + path: '/code-gen/edit/:tableId', + }, + { + component: '/system/role-assign/index', + meta: { + activePath: '/system/role', + icon: 'eos-icons:role-binding-outlined', + title: '分配角色', + hideInMenu: true, + }, + name: 'RoleAssign', + path: '/system/role-assign/:roleId', + }, + + // 任务详情 + { + component: '/tasks/detail', + meta: { + // activePath: '/tasks', + icon: 'eos-icons:role-binding-outlined', + title: '任务详情', + hideInMenu: true, + maxNumOfOpenTab: 1, + }, + name: 'TasksDetail', + path: '/tasks/detail', + }, +]; + +/** + * 这里放本地路由 + */ +export const localMenuList: RouteRecordStringComponent[] = [ + /* { + component: 'BasicLayout', + meta: { + order: -1, + title: 'page.dashboard.title', + // 不使用基础布局(仅在顶级生效) + noBasicLayout: true, + }, + name: 'Dashboard', + path: '/', + redirect: '/analytics', + children: [ + { + name: 'Analytics', + path: '/analytics', + component: '/dashboard/analytics/index', + meta: { + affixTab: true, + icon: 'lucide:area-chart', + title: 'page.dashboard.analytics', + }, + }, + { + name: 'Workspace', + path: '/workspace', + component: '/dashboard/workspace/index', + meta: { + icon: 'carbon:workspace', + title: 'page.dashboard.workspace', + }, + }, + // { + // name: 'VbenDocument', + // path: '/vben-admin/document', + // component: 'IFrameView', + // meta: { + // icon: 'lucide:book-open-text', + // iframeSrc: 'https://dapdap.top', + // keepAlive: true, + // title: $t('demos.vben.document'), + // }, + // }, + ], + }, */ + + { + name: 'Analytics', + path: '/analytics', + component: '/dashboard/analytics/index', + meta: { + affixTab: true, + icon: 'lucide:area-chart', + title: 'page.dashboard.analytics', + }, + }, + // { + // component: '/_core/about/index', + // meta: { + // icon: 'lucide:copyright', + // order: 9999, + // title: $t('demos.vben.about'), + // }, + // name: 'About', + // path: '/vben-admin/about', + // }, + ...localRoutes, +]; diff --git a/apps/web-antd/src/router/routes/modules/dashboard.ts b/apps/web-antd/src/router/routes/modules/dashboard.ts new file mode 100644 index 0000000..5254dc6 --- /dev/null +++ b/apps/web-antd/src/router/routes/modules/dashboard.ts @@ -0,0 +1,38 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { $t } from '#/locales'; + +const routes: RouteRecordRaw[] = [ + { + meta: { + icon: 'lucide:layout-dashboard', + order: -1, + title: $t('page.dashboard.title'), + }, + name: 'Dashboard', + path: '/dashboard', + children: [ + { + name: 'Analytics', + path: '/analytics', + component: () => import('#/views/dashboard/analytics/index.vue'), + meta: { + affixTab: true, + icon: 'lucide:area-chart', + title: $t('page.dashboard.analytics'), + }, + }, + { + name: 'Workspace', + path: '/workspace', + component: () => import('#/views/dashboard/workspace/index.vue'), + meta: { + icon: 'carbon:workspace', + title: $t('page.dashboard.workspace'), + }, + }, + ], + }, +]; + +export default routes; diff --git a/apps/web-antd/src/router/routes/modules/vben.ts b/apps/web-antd/src/router/routes/modules/vben.ts new file mode 100644 index 0000000..a54bb8f --- /dev/null +++ b/apps/web-antd/src/router/routes/modules/vben.ts @@ -0,0 +1,90 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { + VBEN_DOC_URL, + VBEN_ELE_PREVIEW_URL, + VBEN_GITHUB_URL, + VBEN_LOGO_URL, + VBEN_NAIVE_PREVIEW_URL, +} from '@vben/constants'; + +import { IFrameView } from '#/layouts'; +import { $t } from '#/locales'; + +const routes: RouteRecordRaw[] = [ + { + meta: { + badgeType: 'dot', + icon: VBEN_LOGO_URL, + order: 9998, + title: $t('demos.vben.title'), + }, + name: 'VbenProject', + path: '/vben-admin', + children: [ + { + name: 'VbenAbout', + path: '/vben-admin/about', + component: () => import('#/views/_core/about/index.vue'), + meta: { + icon: 'lucide:copyright|offline', + title: $t('demos.vben.about'), + }, + }, + { + name: 'VbenDocument', + path: '/vben-admin/document', + component: IFrameView, + meta: { + icon: 'lucide:book-open-text|offline', + link: VBEN_DOC_URL, + title: $t('demos.vben.document'), + }, + }, + { + name: 'VbenGithub', + path: '/vben-admin/github', + component: IFrameView, + meta: { + icon: 'mdi:github', + link: VBEN_GITHUB_URL, + title: 'Github', + }, + }, + { + name: 'VbenNaive', + path: '/vben-admin/naive', + component: IFrameView, + meta: { + badgeType: 'dot', + icon: 'logos:naiveui', + link: VBEN_NAIVE_PREVIEW_URL, + title: $t('demos.vben.naive-ui'), + }, + }, + { + name: 'VbenElementPlus', + path: '/vben-admin/ele', + component: IFrameView, + meta: { + badgeType: 'dot', + icon: 'logos:element', + link: VBEN_ELE_PREVIEW_URL, + title: $t('demos.vben.element-plus'), + }, + }, + ], + }, + { + name: 'VbenAbout', + path: '/vben-admin/about', + component: () => import('#/views/_core/about/index.vue'), + meta: { + icon: 'lucide:copyright', + title: $t('demos.vben.about'), + order: 9999, + }, + }, +]; + +export default routes; diff --git a/apps/web-antd/src/router/routes/workflow.ts b/apps/web-antd/src/router/routes/workflow.ts new file mode 100644 index 0000000..d879066 --- /dev/null +++ b/apps/web-antd/src/router/routes/workflow.ts @@ -0,0 +1,33 @@ +/* + * @Author: Mm + * @Date: 2025-05-07 15:49:34 + * @LastEditors: Mm + * @LastEditTime: 2025-05-19 09:42:00 + * 不写bug的程序员不是一个好程序员! + */ + +import type { RouteRecordRaw } from 'vue-router'; + +import { $t } from '#/locales'; + +// 流程设计 +const workflowRoutes: RouteRecordRaw[] = [ + { + component: () => import('#/views/admin/FormProcessDesign.vue'), + path: '/admin/design', + name: 'AdminDesign', + meta: { + title: '表单流程设计', + icon: 'ant-design:form-outlined', + }, + }, + { + path: '/mbinitiate', + name: 'mbinitiate', + component: () => import('#/views/workspace/MbInitiateProcess.vue'), + meta: { title: '发起审批'}, + }, + +]; + +export { workflowRoutes }; diff --git a/apps/web-antd/src/store/auth.ts b/apps/web-antd/src/store/auth.ts new file mode 100644 index 0000000..379311b --- /dev/null +++ b/apps/web-antd/src/store/auth.ts @@ -0,0 +1,161 @@ +/* + * @Author: Mm + * @Date: 2025-05-06 14:08:27 + * @LastEditors: Mm + * @LastEditTime: 2025-06-05 19:14:26 + * 不写bug的程序员不是一个好程序员! + */ +import type { LoginAndRegisterParams } from '@vben/common-ui'; +import type { UserInfo } from '@vben/types'; + +import { ref } from 'vue'; +import { useRouter } from 'vue-router'; + +import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants'; +import { resetAllStores, useAccessStore, useUserStore } from '@vben/stores'; + +import { notification } from 'ant-design-vue'; +import { defineStore } from 'pinia'; + +import { doLogout, getUserInfoApi, loginApi, seeConnectionClose } from '#/api'; +import { $t } from '#/locales'; + +import { useDictStore } from './dict'; +import vuexStore from './vuex.js' + + +export const useAuthStore = defineStore('auth', () => { + const accessStore = useAccessStore(); + const userStore = useUserStore(); + const router = useRouter(); + + const loginLoading = ref(false); + + /** + * 异步处理登录操作 + * Asynchronously handle the login process + * @param params 登录表单数据 + */ + async function authLogin( + params: LoginAndRegisterParams, + onSuccess?: () => Promise | void, + ) { + // 异步处理用户登录操作并获取 accessToken + let userInfo: null | UserInfo = null; + try { + loginLoading.value = true; + const { access_token,imLoginVo } = await loginApi(params); + + localStorage.setItem('im_access_token', imLoginVo?.im_access_token); + localStorage.setItem('im_refresh_token', imLoginVo?.im_refresh_token); + // 将 accessToken 存储到 accessStore 中 + accessStore.setAccessToken(access_token); + accessStore.setRefreshToken(access_token); + + // 获取用户信息并存储到 accessStore 中 + userInfo = await fetchUserInfo(); + const userRes = { + id: userInfo.userId, + name: userInfo.username, + avatar: userInfo.avatar, + position: undefined, + type: 'user', + }; + vuexStore.state.loginUser = userRes; + localStorage.setItem('loginUser', JSON.stringify(userRes)); + + /** + * 设置用户信息 + */ + userStore.setUserInfo(userInfo); + /** + * 在这里设置权限 + */ + accessStore.setAccessCodes(userInfo.permissions); + + if (accessStore.loginExpired) { + accessStore.setLoginExpired(false); + } else { + onSuccess ? await onSuccess?.() : await router.push(DEFAULT_HOME_PATH); + } + + if (userInfo?.realName) { + notification.success({ + description: `${$t('authentication.loginSuccessDesc')}:${userInfo?.realName}`, + duration: 3, + message: $t('authentication.loginSuccess'), + }); + } + } finally { + loginLoading.value = false; + } + + return { + userInfo, + }; + } + + async function logout(redirect: boolean = true) { + try { + await seeConnectionClose(); + await doLogout(); + } catch (error) { + console.error(error); + } finally { + resetAllStores(); + accessStore.setLoginExpired(false); + + // 回登陆页带上当前路由地址 + await router.replace({ + path: LOGIN_PATH, + query: redirect + ? { + redirect: encodeURIComponent(router.currentRoute.value.fullPath), + } + : {}, + }); + } + } + + async function fetchUserInfo() { + const backUserInfo = await getUserInfoApi(); + /** + * 登录超时的情况 + */ + if (!backUserInfo) { + throw new Error('获取用户信息失败.'); + } + const { permissions = [], roles = [], user } = backUserInfo; + /** + * 从后台user -> vben user转换 + */ + const userInfo: UserInfo = { + avatar: user.avatar ?? '', + permissions, + realName: user.nickName, + roles, + userId: user.userId, + username: user.userName, + }; + userStore.setUserInfo(userInfo); + /** + * 需要重新加载字典 + * 比如退出登录切换到其他租户 + */ + const dictStore = useDictStore(); + dictStore.resetCache(); + return userInfo; + } + + function $reset() { + loginLoading.value = false; + } + + return { + $reset, + authLogin, + fetchUserInfo, + loginLoading, + logout, + }; +}); diff --git a/apps/web-antd/src/store/dict.ts b/apps/web-antd/src/store/dict.ts new file mode 100644 index 0000000..3d7f4ff --- /dev/null +++ b/apps/web-antd/src/store/dict.ts @@ -0,0 +1,105 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import type { DictData } from '#/api/system/dict/dict-data-model'; + +import { reactive } from 'vue'; + +import { defineStore } from 'pinia'; + +/** + * antd使用 select和radio通用 + * 本质上是对DictData的拓展 + */ +export interface DictOption extends DictData { + disabled?: boolean; + label: string; + value: number | string; +} + +/** + * 将字典数据转为Options + * @param data 字典数据 + * @param formatNumber 是否需要将value格式化为number类型 + * @returns options + */ +export function dictToOptions( + data: DictData[], + formatNumber = false, +): DictOption[] { + return data.map((item) => ({ + ...item, + label: item.dictLabel, + value: formatNumber ? Number(item.dictValue) : item.dictValue, + })); +} + +export const useDictStore = defineStore('app-dict', () => { + /** + * select radio checkbox等使用 只能为固定格式{label, value} + */ + const dictOptionsMap = reactive(new Map()); + /** + * 添加一个字典请求状态的缓存 + * + * 主要解决多次请求重复api的问题(不能用abortController 会导致除了第一个其他的获取的全为空) + * 比如在一个页面 index表单 modal drawer总共会请求三次 但是获取的都是一样的数据 + * 相当于加锁 保证只有第一次请求的结果能拿到 + */ + const dictRequestCache = reactive( + new Map>(), + ); + + function getDictOptions(dictName: string): DictOption[] { + if (!dictName) return []; + // 没有key 添加一个空数组 + if (!dictOptionsMap.has(dictName)) { + dictOptionsMap.set(dictName, []); + } + // 这里拿到的就不可能为空了 + return dictOptionsMap.get(dictName)!; + } + + function resetCache() { + dictOptionsMap.clear(); + } + + /** + * 核心逻辑 + * + * 不能直接粗暴使用set 会导致之前return的空数组跟现在的数组指向不是同一个地址 数据也就为空了 + * + * 判断是否已经存在key 并且数组长度为0 说明该次要处理的数据是return的空数组 直接push(不修改指向) + * 否则 直接set + * + */ + function setDictInfo( + dictName: string, + dictValue: DictData[], + formatNumber = false, + ) { + if ( + dictOptionsMap.has(dictName) && + dictOptionsMap.get(dictName)?.length === 0 + ) { + dictOptionsMap + .get(dictName) + ?.push(...dictToOptions(dictValue, formatNumber)); + } else { + dictOptionsMap.set(dictName, dictToOptions(dictValue, formatNumber)); + } + } + + function $reset() { + /** + * doNothing + */ + } + + return { + $reset, + dictOptionsMap, + dictRequestCache, + getDictOptions, + resetCache, + setDictInfo, + }; +}); diff --git a/apps/web-antd/src/store/index.ts b/apps/web-antd/src/store/index.ts new file mode 100644 index 0000000..7750334 --- /dev/null +++ b/apps/web-antd/src/store/index.ts @@ -0,0 +1,2 @@ +export * from './auth'; +export * from './notify'; diff --git a/apps/web-antd/src/store/notify.ts b/apps/web-antd/src/store/notify.ts new file mode 100644 index 0000000..c1c513d --- /dev/null +++ b/apps/web-antd/src/store/notify.ts @@ -0,0 +1,158 @@ +/* + * @Author: Mm + * @Date: 2025-05-06 14:08:27 + * @LastEditors: Mm + * @LastEditTime: 2025-06-13 15:12:10 + * 不写bug的程序员不是一个好程序员! + */ +import type { NotificationItem } from '@vben/layouts'; + +import { computed, ref, watch } from 'vue'; + +import { useAppConfig } from '@vben/hooks'; +import { SvgMessageUrl } from '@vben/icons'; +import { $t } from '@vben/locales'; +import { useAccessStore, useUserStore } from '@vben/stores'; + +import { useEventSource } from '@vueuse/core'; +import { notification } from 'ant-design-vue'; +import dayjs from 'dayjs'; +import { defineStore } from 'pinia'; + +const { apiURL, clientId, sseEnable } = useAppConfig( + import.meta.env, + import.meta.env.PROD, +); + +export const useNotifyStore = defineStore( + 'app-notify', + () => { + /** + * return才会被持久化 存储全部消息 + */ + const notificationList = ref([]); + + const userStore = useUserStore(); + const userId = computed(() => { + return userStore.userInfo?.userId || '0'; + }); + + const notifications = computed(() => { + return notificationList.value.filter( + (item) => item.userId === userId.value, + ); + }); + + /** + * 开始监听sse消息 + */ + function startListeningMessage() { + /** + * 未开启 不监听 + */ + if (!sseEnable) { + return; + } + const accessStore = useAccessStore(); + const token = accessStore.accessToken; + + const sseAddr = `${apiURL}/resource/sse?clientid=${clientId}&Authorization=Bearer ${token}`; + + const { data } = useEventSource(sseAddr, [], { + autoReconnect: { + delay: 1000, + onFailed() { + console.error('sse重连失败.'); + }, + retries: 3, + }, + }); + + watch(data, (newV) => { + const { message, type } = newV? JSON.parse(newV):{}; + if (!message) return; + console.log(`接收到消息: ${message}`); + + notification.success({ + description: message, + duration: 3, + message: $t('component.notice.received'), + }); + + notificationList.value.unshift({ + // avatar: `https://api.multiavatar.com/${random(0, 10_000)}.png`, 随机头像 + avatar: SvgMessageUrl, + date: dayjs().format('YYYY-MM-DD HH:mm:ss'), + isRead: false, + message, + type, + title: $t('component.notice.title'), + userId: userId.value, + }); + + // 需要手动置空 vue3在值相同时不会触发watch + data.value = null; + }); + } + + /** + * 设置全部已读 + */ + function setAllRead() { + notificationList.value + .filter((item) => item.userId === userId.value) + .forEach((item) => { + item.isRead = true; + }); + } + + /** + * 设置单条消息已读 + * @param item 通知 + */ + function setRead(item: NotificationItem) { + !item.isRead && (item.isRead = true); + } + + /** + * 清空全部消息 + */ + function clearAllMessage() { + notificationList.value = notificationList.value.filter( + (item) => item.userId !== userId.value, + ); + } + + /** + * 只需要空实现即可 + * 否则会在退出登录清空所有 + */ + function $reset() { + // notificationList.value = []; + } + /** + * 显示小圆点 + */ + const showDot = computed(() => + notificationList.value + .filter((item) => item.userId === userId.value) + .some((item) => !item.isRead), + ); + + return { + $reset, + clearAllMessage, + notificationList, + notifications, + setAllRead, + setRead, + showDot, + startListeningMessage, + }; + }, + { + persist: { + pick: ['notificationList'], + }, + }, +); diff --git a/apps/web-antd/src/store/tenant.ts b/apps/web-antd/src/store/tenant.ts new file mode 100644 index 0000000..2fb2615 --- /dev/null +++ b/apps/web-antd/src/store/tenant.ts @@ -0,0 +1,44 @@ +import type { TenantOption } from '#/api/core/auth'; + +import { ref } from 'vue'; + +import { defineStore } from 'pinia'; + +import { tenantList as tenantListApi } from '#/api/core/auth'; + +/** + * 用于超级管理员切换租户 + */ +export const useTenantStore = defineStore('app-tenant', () => { + // 是否已经选中租户 + const checked = ref(false); + // 是否开启租户功能 + const tenantEnable = ref(true); + const tenantList = ref([]); + + // 初始化 获取租户信息 + async function initTenant() { + const { tenantEnabled, voList } = await tenantListApi(); + tenantEnable.value = tenantEnabled; + tenantList.value = voList; + } + + async function setChecked(_checked: boolean) { + checked.value = _checked; + } + + function $reset() { + checked.value = false; + tenantEnable.value = true; + tenantList.value = []; + } + + return { + $reset, + checked, + initTenant, + setChecked, + tenantEnable, + tenantList, + }; +}); diff --git a/apps/web-antd/src/store/vuex.js b/apps/web-antd/src/store/vuex.js new file mode 100644 index 0000000..a28ff43 --- /dev/null +++ b/apps/web-antd/src/store/vuex.js @@ -0,0 +1,33 @@ +/* + * @Author: Mm + * @Date: 2025-05-07 17:00:20 + * @LastEditors: Mm + * @LastEditTime: 2025-05-07 17:00:42 + * 不写bug的程序员不是一个好程序员! + */ +import * as Vuex from 'vuex'; + +export default Vuex.createStore({ + state: { + nodeMap: new Map(), + isEdit: null, + loginUser: JSON.parse(localStorage.getItem('loginUser') || '{}'), + selectedNode: {}, + selectFormItem: null, + design: {}, + }, + mutations: { + selectedNode(state, val) { + state.selectedNode = val; + }, + loadForm(state, val) { + state.design = val; + }, + setIsEdit(state, val) { + state.isEdit = val; + }, + }, + getters: {}, + actions: {}, + modules: {}, +}); diff --git a/apps/web-antd/src/utils/ProcessUtil.js b/apps/web-antd/src/utils/ProcessUtil.js new file mode 100644 index 0000000..370f835 --- /dev/null +++ b/apps/web-antd/src/utils/ProcessUtil.js @@ -0,0 +1,77 @@ +//获取流程结果标签样式 +export function getProcTag(result){ + switch (result){ + case 'RUNNING': + return '' + case 'COMPLETE': + case 'PASS': + return 'success' + case 'CANCEL': + return 'info' + case 'REFUSE': + return 'danger' + } +} + +export function getTaskResult(task){ + if (task.isFuture) return {text: '等待中...', type: 'info'} + if (task.result === 'agree') { + return {text: '已同意', type: 'success'} + } else if (task.result === 'complete') { + return {text: '已办理', type: 'success'} + } else if (task.result === 'refuse') { + return {text: '已拒绝', type: 'danger'} + } else if (task.result === 'transfer') { + return {text: '已转交', type: 'primary'} + }else if (task.result === 'recall') { + return {text: '已退回', type: 'warning'} + } else if (!task.result && task.finishTime) { + return {text: '已取消', type: 'info'} + } else { + return {text: '处理中', type: 'primary'} + } +} +//判断是否为主要业务节点 +export function isPrimaryNode(node){ + return node && node.type && !(isBranchNode(node) || isEmptyNode(node)); +} +export function isBranchNode(node){ + return node && (node.type === 'CONDITIONS' || node.type === 'CONCURRENTS' || node.type === 'INCLUSIVES'); +} +export function isEmptyNode(node){ + return node && (node.type === 'EMPTY') +} +//是分支节点 +export function isConditionNode(node){ + return node.type === 'CONDITIONS'; +} +//是分支节点 +export function isBranchSubNode(node){ + return node && (node.type === 'CONDITION' || node.type === 'CONCURRENT' || node.type === 'INCLUSIVE'); +} +export function isConcurrentNode(node){ + return node.type === 'CONCURRENTS' +} + +export function isInclusiveNode(node){ + return node.type === 'INCLUSIVES' +} + +export function forEachNode(node, callback){ + if (isBranchNode(node)){ + if (callback(node)){return} + node.branchs.map(branchNode => { + if (callback(branchNode)){return} + forEachNode(branchNode.children, callback) + }) + forEachNode(node.children, callback) + }else if (isPrimaryNode(node) || isEmptyNode(node) || isBranchSubNode(node)){ + if (callback(node)){return} + forEachNode(node.children, callback) + } +} + +export default { + forEachNode, isPrimaryNode, isBranchNode, isEmptyNode, + isConditionNode, isBranchSubNode, isConcurrentNode, isInclusiveNode +} diff --git a/apps/web-antd/src/utils/dict.ts b/apps/web-antd/src/utils/dict.ts new file mode 100644 index 0000000..03dbde6 --- /dev/null +++ b/apps/web-antd/src/utils/dict.ts @@ -0,0 +1,70 @@ +import { dictDataInfo } from '#/api/system/dict/dict-data'; +import { useDictStore } from '#/store/dict'; + +/** + * 抽取公共逻辑的基础方法 + * @param dictName 字典名称 + * @param dataGetter 获取字典数据的函数 + * @param formatNumber 是否格式化字典value为number类型 + * @returns 数据 + */ +function fetchAndCacheDictData( + dictName: string, + dataGetter: () => T[], + formatNumber = false, +): T[] { + const { dictRequestCache, setDictInfo } = useDictStore(); + // 有调用方决定如何获取数据 + const dataList = dataGetter(); + + // 检查请求状态缓存 + if (dataList.length === 0 && !dictRequestCache.has(dictName)) { + dictRequestCache.set( + dictName, + dictDataInfo(dictName) + .then((resp) => { + // 缓存到store 这样就不用重复获取了 + // 内部处理了push的逻辑 这里不用push + setDictInfo(dictName, resp, formatNumber); + }) + .finally(() => { + // 移除请求状态缓存 + /** + * 这里主要判断字典item为空的情况(无奈兼容 不给字典item本来就是错误用法) + * 会导致if一直进入逻辑导致接口无限刷新 + * 在这里dictList为空时 不删除缓存 + */ + if (dataList.length > 0) { + dictRequestCache.delete(dictName); + } + }), + ); + } + return dataList; +} + +/** + * 这里是提供给渲染标签使用的方法 + * @deprecated 使用getDictOptions代替 于下个版本删除 + * @param dictName 字典名称 + * @returns 字典信息 + */ +export function getDict(dictName: string) { + const { getDictOptions } = useDictStore(); + return fetchAndCacheDictData(dictName, () => getDictOptions(dictName)); +} + +/** + * 一般是Select, Radio, Checkbox等组件使用 + * @param dictName 字典名称 + * @param formatNumber 是否格式化字典value为number类型 + * @returns Options数组 + */ +export function getDictOptions(dictName: string, formatNumber = false) { + const { getDictOptions } = useDictStore(); + return fetchAndCacheDictData( + dictName, + () => getDictOptions(dictName), + formatNumber, + ); +} diff --git a/apps/web-antd/src/utils/encryption/crypto.ts b/apps/web-antd/src/utils/encryption/crypto.ts new file mode 100644 index 0000000..34696d1 --- /dev/null +++ b/apps/web-antd/src/utils/encryption/crypto.ts @@ -0,0 +1,80 @@ +import CryptoJS from 'crypto-js'; + +function randomUUID() { + const chars = [ + ...'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', + ]; + const uuid = Array.from({ length: 36 }); + let rnd = 0; + let r: number; + for (let i = 0; i < 36; i++) { + if (i === 8 || i === 13 || i === 18 || i === 23) { + uuid[i] = '-'; + } else if (i === 14) { + uuid[i] = '4'; + } else { + if (rnd <= 0x02) + rnd = Math.trunc(0x2_00_00_00 + Math.random() * 0x1_00_00_00); + r = rnd & 16; + rnd = rnd >> 4; + uuid[i] = chars[i === 19 ? (r & 0x3) | 0x8 : r]; + } + } + return uuid.join('').replaceAll('-', '').toLowerCase(); +} + +/** + * 随机生成aes 密钥 + * + * @returns aes 密钥 + */ +export function generateAesKey() { + return CryptoJS.enc.Utf8.parse(randomUUID()); +} + +/** + * base64编码 + * @param str + * @returns base64编码 + */ +export function encryptBase64(str: CryptoJS.lib.WordArray) { + return CryptoJS.enc.Base64.stringify(str); +} + +/** + * 使用公钥加密 + * @param message 加密内容 + * @param aesKey aesKey + * @returns 使用公钥加密 + */ +export function encryptWithAes( + message: string, + aesKey: CryptoJS.lib.WordArray, +) { + const encrypted = CryptoJS.AES.encrypt(message, aesKey, { + mode: CryptoJS.mode.ECB, + padding: CryptoJS.pad.Pkcs7, + }); + return encrypted.toString(); +} + +/** + * 解密base64 + */ +export function decryptBase64(str: string) { + return CryptoJS.enc.Base64.parse(str); +} + +/** + * 使用密钥对数据进行解密 + */ +export function decryptWithAes( + message: string, + aesKey: CryptoJS.lib.WordArray, +) { + const decrypted = CryptoJS.AES.decrypt(message, aesKey, { + mode: CryptoJS.mode.ECB, + padding: CryptoJS.pad.Pkcs7, + }); + return decrypted.toString(CryptoJS.enc.Utf8); +} diff --git a/apps/web-antd/src/utils/encryption/jsencrypt.ts b/apps/web-antd/src/utils/encryption/jsencrypt.ts new file mode 100644 index 0000000..2e1e6a6 --- /dev/null +++ b/apps/web-antd/src/utils/encryption/jsencrypt.ts @@ -0,0 +1,31 @@ +// 密钥对生成 http://web.chacuo.net/netrsakeypair +import { useAppConfig } from '@vben/hooks'; + +import JSEncrypt from 'jsencrypt'; + +const { rsaPrivateKey, rsaPublicKey } = useAppConfig( + import.meta.env, + import.meta.env.PROD, +); + +/** + * 加密 + * @param txt 需要加密的数据 + * @returns 加密后的数据 + */ +export function encrypt(txt: string) { + const instance = new JSEncrypt(); + instance.setPublicKey(rsaPublicKey); + return instance.encrypt(txt); +} + +/** + * 解密 + * @param txt 需要解密的数据 + * @returns 解密后的数据 + */ +export function decrypt(txt: string) { + const instance = new JSEncrypt(); + instance.setPrivateKey(rsaPrivateKey); + return instance.decrypt(txt); +} diff --git a/apps/web-antd/src/utils/file/base64Conver.ts b/apps/web-antd/src/utils/file/base64Conver.ts new file mode 100644 index 0000000..3a487c3 --- /dev/null +++ b/apps/web-antd/src/utils/file/base64Conver.ts @@ -0,0 +1,46 @@ +/** + * @description: base64 to blob + */ +export function dataURLtoBlob(base64Buf: string): Blob { + const arr = base64Buf.split(','); + const typeItem = arr[0]; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const mime = typeItem!.match(/:(.*?);/)![1]; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const bstr = window.atob(arr[1]!); + let n = bstr.length; + const u8arr = new Uint8Array(n); + while (n--) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + u8arr[n] = bstr.codePointAt(n)!; + } + return new Blob([u8arr], { type: mime }); +} + +/** + * img url to base64 + * @param url + */ +export function urlToBase64(url: string, mineType?: string): Promise { + return new Promise((resolve, reject) => { + let canvas = document.createElement('CANVAS') as HTMLCanvasElement | null; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const ctx = canvas!.getContext('2d'); + + const img = new Image(); + img.crossOrigin = ''; + img.addEventListener('load', () => { + if (!canvas || !ctx) { + // eslint-disable-next-line prefer-promise-reject-errors + return reject(); + } + canvas.height = img.height; + canvas.width = img.width; + ctx.drawImage(img, 0, 0); + const dataURL = canvas.toDataURL(mineType || 'image/png'); + canvas = null; + resolve(dataURL); + }); + img.src = url; + }); +} diff --git a/apps/web-antd/src/utils/file/download.ts b/apps/web-antd/src/utils/file/download.ts new file mode 100644 index 0000000..56db953 --- /dev/null +++ b/apps/web-antd/src/utils/file/download.ts @@ -0,0 +1,268 @@ +import type { VbenFormProps } from '#/adapter/form'; + +import { $t } from '@vben/locales'; +import { cloneDeep, formatDate } from '@vben/utils'; + +import { message } from 'ant-design-vue'; +import { isFunction } from 'lodash-es'; + +import { dataURLtoBlob, urlToBase64 } from './base64Conver'; + +/** + * + * @deprecated 无法处理区间选择器数据 请使用commonDownloadExcel + * + * 下载excel文件 + * @param [func] axios函数 + * @param [fileName] 文件名称 不需要带xlsx后缀 + * @param [requestData] 请求参数 + * @param [withRandomName] 是否带随机文件名 + * + * @return void + */ +export async function downloadExcel( + func: (data?: any) => Promise, + fileName: string, + requestData: any = {}, + withRandomName = true, +) { + const hideLoading = message.loading($t('pages.common.downloadLoading'), 0); + try { + const data = await func(requestData); + downloadExcelFile(data, fileName, withRandomName); + } catch (error) { + console.error(error); + } finally { + hideLoading(); + } +} + +/** + * 源码同packages\@core\ui-kit\form-ui\src\components\form-actions.vue + * @param values 表单值 + * @param fieldMappingTime 区间选择器 字段映射 + * @returns 格式化后的值 + */ +function handleRangeTimeValue( + values: Record, + fieldMappingTime: VbenFormProps['fieldMappingTime'], +) { + // 需要深拷贝 可能是readonly的 + values = cloneDeep(values); + if (!fieldMappingTime || !Array.isArray(fieldMappingTime)) { + return values; + } + + fieldMappingTime.forEach( + ([field, [startTimeKey, endTimeKey], format = 'YYYY-MM-DD']) => { + if (startTimeKey && endTimeKey && values[field] === null) { + Reflect.deleteProperty(values, startTimeKey); + Reflect.deleteProperty(values, endTimeKey); + // delete values[startTimeKey]; + // delete values[endTimeKey]; + } + + if (!values[field]) { + Reflect.deleteProperty(values, field); + // delete values[field]; + return; + } + + const [startTime, endTime] = values[field]; + if (format === null) { + values[startTimeKey] = startTime; + values[endTimeKey] = endTime; + } else if (isFunction(format)) { + values[startTimeKey] = format(startTime, startTimeKey); + values[endTimeKey] = format(endTime, endTimeKey); + } else { + const [startTimeFormat, endTimeFormat] = Array.isArray(format) + ? format + : [format, format]; + + values[startTimeKey] = startTime + ? formatDate(startTime, startTimeFormat) + : undefined; + values[endTimeKey] = endTime + ? formatDate(endTime, endTimeFormat) + : undefined; + } + // delete values[field]; + Reflect.deleteProperty(values, field); + }, + ); + return values; +} + +export interface DownloadExcelOptions { + // 是否随机文件名(带时间戳) + withRandomName?: boolean; + // 区间选择器 字段映射 + fieldMappingTime?: VbenFormProps['fieldMappingTime']; +} + +/** + * 通用下载excel方法 + * @param api 后端下载接口 + * @param fileName 文件名 不带拓展名 + * @param requestData 请求参数 + * @param options 下载选项 + */ +export async function commonDownloadExcel( + api: (data?: any) => Promise, + fileName: string, + requestData: any = {}, + options: DownloadExcelOptions = {}, +) { + const hideLoading = message.loading($t('pages.common.downloadLoading'), 0); + try { + const { withRandomName = true, fieldMappingTime } = options; + // 需要处理时间字段映射 + const data = await api(handleRangeTimeValue(requestData, fieldMappingTime)); + downloadExcelFile(data, fileName, withRandomName); + } catch (error) { + console.error(error); + } finally { + hideLoading(); + } +} + +export function downloadExcelFile( + data: BlobPart, + filename: string, + withRandomName = true, +) { + let realFileName = filename; + if (withRandomName) { + realFileName = `${filename}-${Date.now()}.xlsx`; + } + downloadByData(data, realFileName); +} + +/** + * Download online pictures + * @param url + * @param filename + * @param mime + * @param bom + */ +export function downloadByOnlineUrl( + url: string, + filename: string, + mime?: string, + bom?: BlobPart, +) { + urlToBase64(url).then((base64) => { + downloadByBase64(base64, filename, mime, bom); + }); +} + +/** + * Download pictures based on base64 + * @param buf + * @param filename + * @param mime + * @param bom + */ +export function downloadByBase64( + buf: string, + filename: string, + mime?: string, + bom?: BlobPart, +) { + const base64Buf = dataURLtoBlob(buf); + downloadByData(base64Buf, filename, mime, bom); +} + +/** + * Download according to the background interface file stream + * @param {*} data + * @param {*} filename + * @param {*} mime + * @param {*} bom + */ +export function downloadByData( + data: BlobPart, + filename: string, + mime?: string, + bom?: BlobPart, +) { + const blobData = bom === undefined ? [data] : [bom, data]; + const blob = new Blob(blobData, { type: mime || 'application/octet-stream' }); + + const blobURL = window.URL.createObjectURL(blob); + const tempLink = document.createElement('a'); + tempLink.style.display = 'none'; + tempLink.href = blobURL; + tempLink.setAttribute('download', filename); + if (tempLink.download === undefined) { + tempLink.setAttribute('target', '_blank'); + } + document.body.append(tempLink); + tempLink.click(); + tempLink.remove(); + window.URL.revokeObjectURL(blobURL); +} + +export function openWindow( + url: string, + opt?: { + noopener?: boolean; + noreferrer?: boolean; + target?: '_blank' | '_self' | string; + }, +) { + const { noopener = true, noreferrer = true, target = '__blank' } = opt || {}; + const feature: string[] = []; + + noopener && feature.push('noopener=yes'); + noreferrer && feature.push('noreferrer=yes'); + + window.open(url, target, feature.join(',')); +} + +/** + * Download file according to file address + * @param {*} sUrl + */ +export function downloadByUrl({ + fileName, + target = '_blank', + url, +}: { + fileName?: string; + target?: '_blank' | '_self'; + url: string; +}): boolean { + const isChrome = window.navigator.userAgent.toLowerCase().includes('chrome'); + const isSafari = window.navigator.userAgent.toLowerCase().includes('safari'); + + if (/iP/.test(window.navigator.userAgent)) { + console.error('Your browser does not support download!'); + return false; + } + if (isChrome || isSafari) { + const link = document.createElement('a'); + link.href = url; + link.target = target; + + if (link.download !== undefined) { + link.download = + // eslint-disable-next-line unicorn/prefer-string-slice + fileName || url.substring(url.lastIndexOf('/') + 1, url.length); + } + + if (document.createEvent) { + const e = document.createEvent('MouseEvents'); + e.initEvent('click', true, true); + link.dispatchEvent(e); + return true; + } + } + if (!url.includes('?')) { + url += '?download'; + } + + openWindow(url, { target }); + return true; +} diff --git a/apps/web-antd/src/utils/file/index.ts b/apps/web-antd/src/utils/file/index.ts new file mode 100644 index 0000000..dab7d29 --- /dev/null +++ b/apps/web-antd/src/utils/file/index.ts @@ -0,0 +1,31 @@ +/** + * 计算文件大小并以适当单位表示 + * + * 此函数接收一个表示文件大小的数字(以字节为单位),并返回一个格式化后的字符串, + * 该字符串表示文件的大小,以最适合的单位(B, KB, MB, GB, TB)表示 + * + * @param size 文件大小,以字节为单位 + * @param isInteger 是否返回整数大小,默认为false如果设置为true, + * 则返回的大小将不包含小数部分;如果为false,则根据单位的不同, + * 返回最多3位小数的大小 + * @returns 格式化后的文件大小字符串,如"4.5KB"或"3MB" + */ +export function calculateFileSize(size: number, isInteger = false) { + // 定义文件大小的单位数组 + const units = ['B', 'KB', 'MB', 'GB', 'TB']; + // 定义换算基数,1KB = 1024B,1MB = 1024KB,以此类推 + const base = 1024; + + // 初始化单位索引,初始值为0,即默认单位为B + let unitIndex = 0; + // 当文件大小大于等于基数且单位索引未超出单位数组范围时,循环进行单位转换 + while (size >= base && unitIndex < units.length - 1) { + size /= base; + unitIndex++; + } + + // 根据是否需要整数大小,确定输出的精度 + const precision = isInteger ? 0 : Math.min(unitIndex, 3); + // 返回格式化后的文件大小字符串 + return `${size.toFixed(precision)}${units[unitIndex]}`; +} diff --git a/apps/web-antd/src/utils/gogocodeTransfer.js b/apps/web-antd/src/utils/gogocodeTransfer.js new file mode 100644 index 0000000..297f804 --- /dev/null +++ b/apps/web-antd/src/utils/gogocodeTransfer.js @@ -0,0 +1,106 @@ +const eventRegistryMap = new WeakMap() +function getRegistry(instance) { + let events = eventRegistryMap.get(instance) + if (!events) { + eventRegistryMap.set(instance, (events = Object.create(null))) + } + return events +} +export function $on(instance, event, fn) { + if (Array.isArray(event)) { + event.forEach((e) => $on(instance, e, fn)) + } else { + const events = getRegistry(instance) + ;(events[event] || (events[event] = [])).push(fn) + } + return instance +} +export function $once(instance, event, fn) { + const wrapped = (...args) => { + $off(instance, event, wrapped) + fn.call(instance, ...args) + } + wrapped.fn = fn + $on(instance, event, wrapped) + return instance +} +export function $off(instance, event, fn) { + const vm = instance + // all + if (!event) { + eventRegistryMap.set(instance, Object.create(null)) + return vm + } + // array of events + if (Array.isArray(event)) { + event.forEach((e) => $off(instance, e, fn)) + return vm + } + // specific event + const events = getRegistry(instance) + const cbs = events[event] + if (!cbs) { + return vm + } + if (!fn) { + events[event] = undefined + return vm + } + events[event] = cbs.filter((cb) => !(cb === fn || cb.fn === fn)) + return vm +} +export function $emit(instance, event, ...args) { + instance && instance.$emit && instance.$emit(event, ...args) + const cbs = getRegistry(instance)[event] + if (cbs) { + cbs.map((cb) => cb.apply(instance, args)) + } + return instance +} +export function plantRenderPara(params) { + const transProps = { + staticClass: 'class', + staticStyle: 'style', + on: '', + domProps: '', + props: '', + attrs: '', + } + function obj2arr(obj) { + return typeof obj == 'string' + ? [obj] + : Object.keys(obj).map((pk, index) => { + return { [pk]: Object.values(obj)[index] } + }) + } + let result = {} + for (let key in params) { + if (transProps[key] == null) { + if (typeof params[key] == 'object') { + result[key] = obj2arr(params[key]) + } else { + result[key] = params[key] + } + } + } + for (let key in params) { + if (transProps[key] === '') { + if (typeof params[key] == 'object') { + for (let k in params[key]) { + result[k] = params[key][k] + } + } else { + result[key] = params[key] + } + } + } + for (let key in params) { + if (transProps[key]) { + result[transProps[key]] = result[transProps[key]] || [] + result[transProps[key]] = result[transProps[key]].concat( + obj2arr(params[key]) + ) + } + } + return result +} diff --git a/apps/web-antd/src/utils/imageUtil.js b/apps/web-antd/src/utils/imageUtil.js new file mode 100644 index 0000000..ce6644e --- /dev/null +++ b/apps/web-antd/src/utils/imageUtil.js @@ -0,0 +1,113 @@ +/** + * 图片转base64并压缩指定大小 + * @param self + * @param size + */ +export function imgFileZip(self, size, call) { + //创建一个读取文件的对象 + let reader = new FileReader(); + //读取文件,转码 + reader.readAsDataURL(self.files[0]); + reader.onload = function (e) { + let base64 = e.target.result; //转码过后的base64编码 + console.log("压缩前", base64.length / 1024 + 'KB'); + //创建一个图片 + let newImage = new Image(); + let quality = 0.6; //压缩系数0-1之间,压缩到0.9以上会有bug,注意!(可以自行设置) + newImage.src = base64; + newImage.setAttribute("crossOrigin", 'Anonymous'); //url为外域时需要 + let imgWidth, imgHeight; + newImage.onload = function () { + imgWidth = this.width; + imgHeight = this.height; + //给生成图片设置一个默认的宽度(可以自行设置) + let myWidth = 50; + //准备在画布上绘制图片 + let canvas = document.createElement("canvas"); + let ctx = canvas.getContext("2d"); + //判断上传的图片的宽高是否超过设置的默认宽度以及设置同比例的高 + if (Math.max(imgWidth, imgHeight) > myWidth) { + if (imgWidth > imgHeight) { + canvas.width = myWidth; + canvas.height = myWidth * imgHeight / imgWidth; + } else { + canvas.height = myWidth; + canvas.width = myWidth * imgWidth / imgHeight; + } + } else { + canvas.width = imgWidth; + canvas.height = imgHeight; + } + //清空画布 + ctx.clearRect(0, 0, canvas.width, canvas.height); + //开始绘制图片到画布上 + ctx.drawImage(this, 0, 0, canvas.width, canvas.height); + let newBase64 = canvas.toDataURL("image/jpeg", quality);//压缩图片大小(重点代码) + // 获取到当前的图片的大小,然后调整成自己需要的大小,例如说需要200KB-500KB之间(可以自行设置) + while (newBase64.length > 1024 * size && quality > 0) { + quality -= 0.02; + newBase64 = canvas.toDataURL("image/jpeg", quality); + } + call(newBase64); + console.log("压缩后", newBase64.length / 1024 + 'KB') + } + }; + +} + +export function imgZip(path, obj, callback) { + var img = new Image(); + img.src = path; + img.onload = function () { + var that = this; + // 默认按比例压缩 + var w = that.width, h = that.height, scale = w / h; + w = (obj.width || w) / 3; + h = (obj.height || (w / scale)) / 3; + var quality = 0.6; // 默认图片质量为0.7 + // 生成canvas + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('2d'); + // 创建属性节点 + var anw = document.createAttribute("width"); + anw.nodeValue = w; + var anh = document.createAttribute("height"); + anh.nodeValue = h; + canvas.setAttributeNode(anw); + canvas.setAttributeNode(anh); + ctx.drawImage(that, 0, 0, w, h); + // 图像质量 + if (obj.quality && obj.quality <= 1 && obj.quality > 0) { + quality = obj.quality; + } + // quality值越小,所绘制出的图像越模糊 + var base64 = canvas.toDataURL('image/jpeg', quality); + // 回调函数返回base64的值 + callback(base64); + } +} + +export function compressBase64Image(base64Image, compressionRatio) { + return new Promise((resolve, reject) => { + const img = new Image(); + img.src = base64Image; + img.onload = function() { + const canvas = document.createElement('canvas'); + canvas.width = img.width * compressionRatio; + canvas.height = img.height * compressionRatio; + const ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0, canvas.width, canvas.height); + // 将Canvas上的图像转换为PNG格式的Base64 + const compressedBase64 = canvas.toDataURL('image/png', 1); + resolve(compressedBase64); + }; + + img.onerror = function() { + reject("无法加载图像"); + }; + }); +} + +export default { + imgFileZip, imgZip +} diff --git a/apps/web-antd/src/utils/modal.tsx b/apps/web-antd/src/utils/modal.tsx new file mode 100644 index 0000000..a3c3b41 --- /dev/null +++ b/apps/web-antd/src/utils/modal.tsx @@ -0,0 +1,64 @@ +import type { ModalFuncProps } from 'ant-design-vue'; +import type { Rule } from 'ant-design-vue/es/form'; + +import { reactive } from 'vue'; + +import { Alert, Form, Input, Modal } from 'ant-design-vue'; +import { isFunction } from 'lodash-es'; + +export interface ConfirmModalProps extends Omit { + confirmText?: string; + placeholder?: string; + onValidated?: () => Promise; +} + +export function confirmDeleteModal(props: ConfirmModalProps) { + const placeholder = props.placeholder || `输入'确认删除'`; + const confirmText = props.confirmText || '确认删除'; + + const formValue = reactive({ + content: '', + }); + const rulesRef = reactive<{ [key: string]: Rule[] }>({ + content: [ + { + message: '校验不通过', + required: true, + trigger: 'change', + validator(_, value) { + if (value !== confirmText) { + return Promise.reject(new Error('校验不通过')); + } + return Promise.resolve(); + }, + }, + ], + }); + const useForm = Form.useForm; + const { validate, validateInfos } = useForm(formValue, rulesRef); + + Modal.confirm({ + ...props, + centered: true, + content: ( +
    + +
    + + + +
    +
    + ), + okButtonProps: { danger: true, type: 'primary' }, + onOk: async () => { + await validate(); + isFunction(props.onValidated) && props.onValidated(); + }, + title: '提示', + type: 'warning', + }); +} diff --git a/apps/web-antd/src/utils/print.js b/apps/web-antd/src/utils/print.js new file mode 100644 index 0000000..870c94b --- /dev/null +++ b/apps/web-antd/src/utils/print.js @@ -0,0 +1,268 @@ +//将变量绑定打印模板,执行动态渲染 +export function bindVar(printTemplate, variable, domId) { + const varExp = /\${\w+}/gi + //替换基础变量 + const printDom = document.getElementById(domId) + printDom.innerHTML = printTemplate.replace(varExp, mc => { + return variable[mc.substring(2, mc.length - 1).trim()] || mc + }) + const tableDoms = printDom.getElementsByTagName('table') + //根据字段搜索所有表格,动态渲染表格 + for (let td of tableDoms) { + //判断该表格存在于数据内 + if (Array.isArray(variable[td.className])) { + const tbody = td.children[0] + let tr = '' + if (tbody.children.length === 1) { + //可能是没有表头,就直接扫首行 + tr = tbody.children[0].outerHTML + } else { + //按最后一行来处理 + tr = tbody.children[tbody.children.length - 1].outerHTML + } + //判断有没有变量存在,没有就不处理 + if (varExp.test(tr)) { + //行循环 + let tbHtml = '', thHtml = ''; + (variable[td.className] || []).forEach(row => { + tbHtml += tr.replace(varExp, mc => { + return row[mc.substring(2, mc.length - 1).trim()] || mc + }) + }) + //取表头部分 + for (let i = 0; i < tbody.children.length - 1; i++) { + thHtml += tbody.children[i].outerHTML + } + tbody.outerHTML = thHtml + tbHtml + } + } + } + printDom.innerHTML = printDom.innerHTML.replace(varExp, mc => { + //没有值的字段用空代替,这里可以自己选择 + return '' + }) +} + +export function getVal (formData, forms, val) { + forms.forEach(item => { + //表单值,表单配置,要解析成的值 + decodeFieldVal(formData, item, val) + }) +} + +function decodeFieldVal(formData, item, val){ + let value = '' + switch (item.name) { + case 'SpanLayout': + getVal(formData, item.props.items, val) + break + case 'TableList': + value = []; + (formData[item.id] || []).forEach((row, i) => { + value.push({}) + item.props.columns.forEach(col => { + decodeFieldVal(row, col, value[i]) + }) + }) + break + case 'TimeRangePicker': + case 'DateTimeRange': + const v = formData[item.id] + if (Array.isArray(v) && v.length > 1) { + value = v[0] + ' ~ ' + v[1] + } + break + case 'DeptPicker': + case 'UserPicker': + value = String((formData[item.id] || []).map(v => v.name)) + break + case 'MultipleSelect': + value = String(formData[item.id]) + break + case 'SignPanel': + if (formData[item.id]){ + value = `` + } + break + case 'ImageUpload': + formData[item.id].forEach(it => { + value += `` + }) + break + case 'FileUpload': + value = String((formData[item.id] || []).map(v => v.name)) + break + default: + value = formData[item.id] + break + } + val[item.id] = value +} + +/* eslint-disable */ +const Print = function (dom, options) { + if (!(this instanceof Print)) return new Print(dom, options); + + this.options = this.extend({ + 'noPrint': '.no-print' + }, options); + + if ((typeof dom) === "string") { + this.dom = document.querySelector(dom); + } else { + this.isDOM(dom) + this.dom = this.isDOM(dom) ? dom : dom.$el; + } + + this.init(); +}; +Print.prototype = { + init: function () { + var content = this.getStyle() + this.getHtml(); + this.writeIframe(content); + }, + extend: function (obj, obj2) { + for (var k in obj2) { + obj[k] = obj2[k]; + } + return obj; + }, + + getStyle: function () { + var str = "", + styles = document.querySelectorAll('style,link'); + for (var i = 0; i < styles.length; i++) { + str += styles[i].outerHTML; + } + str += ""; + + return str; + }, + + getHtml: function () { + var inputs = document.querySelectorAll('input'); + var textareas = document.querySelectorAll('textarea'); + var selects = document.querySelectorAll('select'); + var canvass = document.querySelectorAll('canvas'); + for (var k = 0; k < inputs.length; k++) { + if (inputs[k].type == "checkbox" || inputs[k].type == "radio") { + if (inputs[k].checked == true) { + inputs[k].setAttribute('checked', "checked") + } else { + inputs[k].removeAttribute('checked') + } + } else if (inputs[k].type == "text") { + inputs[k].setAttribute('value', inputs[k].value) + } else { + inputs[k].setAttribute('value', inputs[k].value) + } + } + + for (var k2 = 0; k2 < textareas.length; k2++) { + if (textareas[k2].type == 'textarea') { + textareas[k2].innerHTML = textareas[k2].value + } + } + + for (var k3 = 0; k3 < selects.length; k3++) { + if (selects[k3].type == 'select-one') { + var child = selects[k3].children; + for (var i in child) { + if (child[i].tagName == 'OPTION') { + if (child[i].selected == true) { + child[i].setAttribute('selected', "selected") + } else { + child[i].removeAttribute('selected') + } + } + } + } + } + //打印canvas图像 + for (var k4 = 0; k4 < canvass.length; k4++) { + var imageURL = canvass[k4].toDataURL("image/png"); + var img = document.createElement("img"); + img.src = imageURL; + img.setAttribute('style', canvass[k4].getAttribute('style')); + img.setAttribute('class', canvass[k4].getAttribute('class')); + canvass[k4].style.display = 'none' + canvass[k4].parentNode.insertBefore(img, canvass[k4].nextElementSibling); + } + // 包裹要打印的元素 + let outerHTML = this.dom.parentElement.innerHTML + //this.wrapperRefDom(this.dom).outerHTML + return outerHTML; + }, + // 向父级元素循环,包裹当前需要打印的元素 + // 防止根级别开头的 css 选择器不生效 + wrapperRefDom: function (refDom) { + let prevDom = null + let currDom = refDom + // 判断当前元素是否在 body 中,不在文档中则直接返回该节点 + if (!this.isInBody(currDom)) return currDom + + while (currDom) { + if (prevDom) { + let element = currDom.cloneNode(false) + element.appendChild(prevDom) + prevDom = element + } else { + prevDom = currDom.cloneNode(true) + } + + currDom = currDom.parentElement + } + + return prevDom + }, + + writeIframe: function (content) { + var w, doc, iframe = document.createElement('iframe'), + f = document.body.appendChild(iframe); + iframe.id = "myIframe"; + iframe.style.width = '100vw'; + iframe.style.height = '100vh'; + w = f.contentWindow || f.contentDocument; + doc = f.contentDocument || f.contentWindow.document; + doc.open(); + doc.write(content); + doc.close(); + var _this = this + iframe.onload = function(){ + _this.toPrint(w); + setTimeout(function () { + document.body.removeChild(iframe) + }, 100) + } + }, + + toPrint: function (frameWindow) { + try { + setTimeout(function () { + frameWindow.focus(); + try { + if (!frameWindow.document.execCommand('print', false, null)) { + frameWindow.print(); + } + } catch (e) { + frameWindow.print(); + } + frameWindow.close(); + }, 10); + } catch (err) { + console.log('err', err); + } + }, + // 检查一个元素是否是 body 元素的后代元素且非 body 元素本身 + isInBody: function (node) { + return (node === document.body) ? false : document.body.contains(node); + }, + isDOM: (typeof HTMLElement === 'object') ? + function (obj) { + return obj instanceof HTMLElement; + } : + function (obj) { + return obj && typeof obj === 'object' && obj.nodeType === 1 && typeof obj.nodeName === 'string'; + } +}; +export default Print diff --git a/apps/web-antd/src/utils/render.tsx b/apps/web-antd/src/utils/render.tsx new file mode 100644 index 0000000..9953e49 --- /dev/null +++ b/apps/web-antd/src/utils/render.tsx @@ -0,0 +1,242 @@ +/* + * @Author: Mm + * @Date: 2025-05-06 14:08:27 + * @LastEditors: hcc + * @LastEditTime: 2025-06-09 10:54:06 + * 不写bug的程序员不是一个好程序员! + */ +import type { Component as ComponentType } from 'vue'; + +import type { DictData } from '#/api/system/dict/dict-data-model'; + +import { h } from 'vue'; + +import { JsonPreview } from '@vben/common-ui'; +import { + AndroidIcon, + BaiduIcon, + ChromeIcon, + DefaultBrowserIcon, + DefaultOsIcon, + DingtalkIcon, + EdgeIcon, + FirefoxIcon, + IconifyIcon, + IPhoneIcon, + LinuxIcon, + MicromessengerIcon, + OperaIcon, + OSXIcon, + QQIcon, + QuarkIcon, + SafariIcon, + UcIcon, + WindowsIcon, +} from '@vben/icons'; + +import { Tag } from 'ant-design-vue'; + +import { DictTag } from '#/components/dict'; + +import { getDictOptions } from './dict'; + +/** + * 渲染标签 + * @param text 文字 + * @param color 颜色 + * @returns render + */ +function renderTag(text: string, color?: string) { + return {text}; +} + +/** + * + * @param tags 标签list + * @param wrap 是否换行显示 + * @param [gap] 间隔 + * @returns render + */ +export function renderTags(tags: string[], wrap = false, gap = 1) { + return ( +
    + {tags.map((tag, index) => { + return
    {renderTag(tag)}
    ; + })} +
    + ); +} + +/** + * + * @param json json对象 接受object/string类型 + * @returns json预览 + */ +export function renderJsonPreview(json: any) { + if (typeof json !== 'object' && typeof json !== 'string') { + return {json}; + } + if (typeof json === 'object') { + return ; + } + try { + const obj = JSON.parse(json); + // 基本数据类型可以被转为json + if (typeof obj !== 'object') { + return {obj}; + } + return ; + } catch { + return {json}; + } +} + +/** + * iconify图标 + * @param icon icon名称 + * @returns render + */ +export function renderIcon(icon: string) { + return ; +} +// 直接渲染文字 +export function renderText(value: string, dictName: string) { + const dictInfo = getDictOptions(dictName); + const current: any = dictInfo.find((item) => item.value === value); + return current.label; +} +/** + * httpMethod标签 + * @param type method类型 + * @returns render + */ +export function renderHttpMethodTag(type: string) { + const method = type.toUpperCase(); + const colors: { [key: string]: string } = { + DELETE: 'red', + GET: 'green', + POST: 'blue', + PUT: 'orange', + }; + + const color = colors[method] ?? 'default'; + const title = `${method}请求`; + + return {title}; +} + +export function renderDictTag(value: string, dicts: DictData[]) { + return ; +} + +/** + * render多个dictTag + * @param value key数组 string[]类型 + * @param dicts 字典数组 + * @param wrap 是否需要换行显示 + * @param [gap] 间隔 + * @returns render + */ +export function renderDictTags( + value: string[], + dicts: DictData[], + wrap = true, + gap = 1, +) { + if (!Array.isArray(value)) { + return
    {value}
    ; + } + return ( +
    + {value.map((item, index) => { + return
    {renderDictTag(item, dicts)}
    ; + })} +
    + ); +} + +/** + * 显示字典标签 一般是table使用 + * @param value 值 + * @param dictName dictName + * @returns tag + */ +export function renderDict(value: string, dictName: string) { + const dictInfo = getDictOptions(dictName); + return renderDictTag(value, dictInfo); +} +export function renderIconSpan( + icon: ComponentType, + value: string, + center = false, + marginLeft = '2px', +) { + const justifyCenter = center ? 'justify-center' : ''; + + return ( + + {h(icon)} + {value} + + ); +} + +const osOptions = [ + { icon: WindowsIcon, value: 'windows' }, + { icon: LinuxIcon, value: 'linux' }, + { icon: OSXIcon, value: 'osx' }, + { icon: AndroidIcon, value: 'android' }, + { icon: IPhoneIcon, value: 'iphone' }, +]; + +/** + * 浏览器图标 + * cn.hutool.http.useragent -> browers + */ +const browserOptions = [ + { icon: ChromeIcon, value: 'chrome' }, + { icon: EdgeIcon, value: 'edge' }, + { icon: FirefoxIcon, value: 'firefox' }, + { icon: OperaIcon, value: 'opera' }, + { icon: SafariIcon, value: 'safari' }, + { icon: MicromessengerIcon, value: 'micromessenger' }, + { icon: MicromessengerIcon, value: 'windowswechat' }, + { icon: QuarkIcon, value: 'quark' }, + { icon: MicromessengerIcon, value: 'wxwork' }, + { icon: QQIcon, value: 'qq' }, + { icon: DingtalkIcon, value: 'dingtalk' }, + { icon: UcIcon, value: 'uc' }, + { icon: BaiduIcon, value: 'baidu' }, +]; + +export function renderOsIcon(os: string, center = false) { + if (!os) { + return; + } + let current = osOptions.find((item) => + os.toLocaleLowerCase().includes(item.value), + ); + // windows要特殊处理 + if (os.toLocaleLowerCase().includes('windows')) { + current = osOptions[0]; + } + const icon = current ? current.icon : DefaultOsIcon; + return renderIconSpan(icon, os, center, '5px'); +} + +export function renderBrowserIcon(browser: string, center = false) { + if (!browser) { + return; + } + const current = browserOptions.find((item) => + browser.toLocaleLowerCase().includes(item.value), + ); + const icon = current ? current.icon : DefaultBrowserIcon; + return renderIconSpan(icon, browser, center, '5px'); +} diff --git a/apps/web-antd/src/utils/utils.js b/apps/web-antd/src/utils/utils.js new file mode 100644 index 0000000..e3cbca2 --- /dev/null +++ b/apps/web-antd/src/utils/utils.js @@ -0,0 +1,94 @@ +/* + * @Author: Mm + * @Date: 2025-05-07 17:08:30 + * @LastEditors: Mm + * @LastEditTime: 2025-05-12 10:12:54 + * 不写bug的程序员不是一个好程序员! + */ + +const $isNotEmpty = function (obj) { + return obj !== undefined && obj !== null && obj !== '' && obj !== 'null'; +}; +/** + * 去抖动函数 + * @param call 回调函数 + * @param cycle 抖动时长 + * @returns {(function(...[*]): void)|*} + */ +const $debounce = function (call, cycle=1000) { + var timer = null; // 创建一个用来存放定时器的变量 + let func = call + return function (...args) { + clearTimeout(timer); //只要触发就清除 + timer = setTimeout(() => { + func.apply(this, args); + }, cycle); + }; +} +/** + * 对对象进行序列化深拷贝 + * @param obj + * @returns {any} + */ +const $deepCopy = function (obj) { + return JSON.parse(JSON.stringify(obj || '{}')) +} + +//判断是否设备为移动端 +const $isMobile = function () { + return window.screen.width < 450; +}; +/** + * 解析资源地址 + * @param url 资源url + * @returns {string} + */ +const $getRes = function (url) { + const reg = /^(http:|https:).*/gi; + // return reg.test(url) ? url : globalProps.BASE_URL + url; + return reg.test(url) ? url : url; +}; +export default{ + install:(app)=>{ + app.config.globalProperties.$isNotEmpty = $isNotEmpty; + app.config.globalProperties.$debounce = $debounce; + app.config.globalProperties.$deepCopy = $deepCopy; + app.config.globalProperties.$isMobile = $isMobile; + app.config.globalProperties.$getRes = $getRes; + + /** + * 移除一个数组值 + * @param value 需要移除得值 + * @returns {number} + */ + Array.prototype.remove = function (value) { + let index = this.indexOf(value); + if (index > -1) { + this.splice(index, 1); + } + return index; + }; + + /** + * 根据key 移除数组里面一个指定值的对象,匹配唯一key + * @param key key + * @param val key对应的值 + * @returns {number} + */ + Array.prototype.removeByKey = function (key, val) { + let index = this.findIndex((value) => value[key] === val); + if (index > -1) { + this.splice(index, 1); + } + return index; + }; + + //对象数组转map + Array.prototype.toMap = function (key) { + let map = new Map(); + this.forEach((v) => map.set(v[key], v)); + return map; + }; + } + +} diff --git a/apps/web-antd/src/views/_core/README.md b/apps/web-antd/src/views/_core/README.md new file mode 100644 index 0000000..8248afe --- /dev/null +++ b/apps/web-antd/src/views/_core/README.md @@ -0,0 +1,3 @@ +# \_core + +此目录包含应用程序正常运行所需的基本视图。这些视图是应用程序布局中使用的视图。 diff --git a/apps/web-antd/src/views/_core/about/index.vue b/apps/web-antd/src/views/_core/about/index.vue new file mode 100644 index 0000000..0ee5243 --- /dev/null +++ b/apps/web-antd/src/views/_core/about/index.vue @@ -0,0 +1,9 @@ + + + diff --git a/apps/web-antd/src/views/_core/authentication/code-login.vue b/apps/web-antd/src/views/_core/authentication/code-login.vue new file mode 100644 index 0000000..45cb1e3 --- /dev/null +++ b/apps/web-antd/src/views/_core/authentication/code-login.vue @@ -0,0 +1,143 @@ + + + diff --git a/apps/web-antd/src/views/_core/authentication/forget-password.vue b/apps/web-antd/src/views/_core/authentication/forget-password.vue new file mode 100644 index 0000000..0958e89 --- /dev/null +++ b/apps/web-antd/src/views/_core/authentication/forget-password.vue @@ -0,0 +1,42 @@ + + + diff --git a/apps/web-antd/src/views/_core/authentication/login.vue b/apps/web-antd/src/views/_core/authentication/login.vue new file mode 100644 index 0000000..a8d8b56 --- /dev/null +++ b/apps/web-antd/src/views/_core/authentication/login.vue @@ -0,0 +1,173 @@ + + + + diff --git a/apps/web-antd/src/views/_core/authentication/oauth-login.vue b/apps/web-antd/src/views/_core/authentication/oauth-login.vue new file mode 100644 index 0000000..0c359ec --- /dev/null +++ b/apps/web-antd/src/views/_core/authentication/oauth-login.vue @@ -0,0 +1,44 @@ + + + diff --git a/apps/web-antd/src/views/_core/authentication/qrcode-login.vue b/apps/web-antd/src/views/_core/authentication/qrcode-login.vue new file mode 100644 index 0000000..23f5f2d --- /dev/null +++ b/apps/web-antd/src/views/_core/authentication/qrcode-login.vue @@ -0,0 +1,10 @@ + + + diff --git a/apps/web-antd/src/views/_core/authentication/register.vue b/apps/web-antd/src/views/_core/authentication/register.vue new file mode 100644 index 0000000..f264c46 --- /dev/null +++ b/apps/web-antd/src/views/_core/authentication/register.vue @@ -0,0 +1,95 @@ + + + diff --git a/apps/web-antd/src/views/_core/fallback/coming-soon.vue b/apps/web-antd/src/views/_core/fallback/coming-soon.vue new file mode 100644 index 0000000..f394930 --- /dev/null +++ b/apps/web-antd/src/views/_core/fallback/coming-soon.vue @@ -0,0 +1,7 @@ + + + diff --git a/apps/web-antd/src/views/_core/fallback/forbidden.vue b/apps/web-antd/src/views/_core/fallback/forbidden.vue new file mode 100644 index 0000000..8ea65fe --- /dev/null +++ b/apps/web-antd/src/views/_core/fallback/forbidden.vue @@ -0,0 +1,9 @@ + + + diff --git a/apps/web-antd/src/views/_core/fallback/internal-error.vue b/apps/web-antd/src/views/_core/fallback/internal-error.vue new file mode 100644 index 0000000..819a47d --- /dev/null +++ b/apps/web-antd/src/views/_core/fallback/internal-error.vue @@ -0,0 +1,9 @@ + + + diff --git a/apps/web-antd/src/views/_core/fallback/not-found.vue b/apps/web-antd/src/views/_core/fallback/not-found.vue new file mode 100644 index 0000000..4d178e9 --- /dev/null +++ b/apps/web-antd/src/views/_core/fallback/not-found.vue @@ -0,0 +1,9 @@ + + + diff --git a/apps/web-antd/src/views/_core/fallback/offline.vue b/apps/web-antd/src/views/_core/fallback/offline.vue new file mode 100644 index 0000000..5de4a88 --- /dev/null +++ b/apps/web-antd/src/views/_core/fallback/offline.vue @@ -0,0 +1,9 @@ + + + diff --git a/apps/web-antd/src/views/_core/oauth-common.ts b/apps/web-antd/src/views/_core/oauth-common.ts new file mode 100644 index 0000000..6627442 --- /dev/null +++ b/apps/web-antd/src/views/_core/oauth-common.ts @@ -0,0 +1,104 @@ +import type { Component } from 'vue'; + +import { + AlipayIcon, + DingdingIcon, + GiteeIcon, + GithubOAuthIcon, + TaobaoIcon, +} from '@vben/icons'; + +import { authBinding } from '#/api/core/auth'; +/** + * @description: 菜单 + * @param key key + * @param title 标题 + * @param description 描述 + * @param extra 按钮文字 + * @param avatar 图标 + * @param color 图标颜色可直接写英文颜色/hex + */ +export interface ListItem { + key: string; + title: string; + description: string; + extra?: string; + avatar?: Component; + color?: string; +} + +/** + * @description: 绑定账号 + * @param source 来源 如gitee github 与后端的social-callback?source=xxx对应 + * @param bound 是否已经绑定 + * @param action 账号绑定回调 + */ +export interface BindItem extends ListItem { + source: string; + bound?: boolean; + action?: (source: string) => Promise; +} + +/** + * todo tenantId + * 绑定授权从userStore.userInfo获取 + * 登录从localStorage获取 + * @param source + */ +async function handleAuthBinding(source: string) { + const tenantId = localStorage.getItem('__oauth_tenant_id') ?? '000000'; + // 这里返回打开授权页面的链接 + const href = await authBinding(source, tenantId); + window.location.href = href; +} + +/** + * 账号绑定 list + * 添加账号绑定只需要在这里增加即可 + * 添加过的项目会在个人主页-绑定账号中显示 + * action不为空的会在登录页显示 + */ +export const accountBindList: BindItem[] = [ + { + avatar: TaobaoIcon, + color: '#ff4000', + description: '绑定淘宝账号', + key: '1', + source: 'taobao', + title: '淘宝', + }, + { + avatar: AlipayIcon, + color: '#2eabff', + description: '绑定支付宝账号', + key: '2', + source: 'alipay', + title: '支付宝', + }, + { + avatar: DingdingIcon, + color: '#2eabff', + description: '绑定钉钉账号', + key: '3', + source: 'ding', + title: '钉钉', + }, + { + action: () => handleAuthBinding('gitee'), + avatar: GiteeIcon, + color: '#c71d23', + description: '绑定GITEE账号', + key: '4', + source: 'gitee', + title: 'GITEE', + }, + { + action: () => handleAuthBinding('github'), + avatar: GithubOAuthIcon, + color: '', + description: '绑定GITHUB账号', + key: '5', + source: 'github', + title: 'GITHUB', + }, +]; diff --git a/apps/web-antd/src/views/_core/profile/components/account-bind.vue b/apps/web-antd/src/views/_core/profile/components/account-bind.vue new file mode 100644 index 0000000..21de3aa --- /dev/null +++ b/apps/web-antd/src/views/_core/profile/components/account-bind.vue @@ -0,0 +1,211 @@ + + + + + diff --git a/apps/web-antd/src/views/_core/profile/components/base-setting.vue b/apps/web-antd/src/views/_core/profile/components/base-setting.vue new file mode 100644 index 0000000..1801b23 --- /dev/null +++ b/apps/web-antd/src/views/_core/profile/components/base-setting.vue @@ -0,0 +1,120 @@ + + + diff --git a/apps/web-antd/src/views/_core/profile/components/online-device.vue b/apps/web-antd/src/views/_core/profile/components/online-device.vue new file mode 100644 index 0000000..926f2ba --- /dev/null +++ b/apps/web-antd/src/views/_core/profile/components/online-device.vue @@ -0,0 +1,52 @@ + + + diff --git a/apps/web-antd/src/views/_core/profile/components/secure-setting.vue b/apps/web-antd/src/views/_core/profile/components/secure-setting.vue new file mode 100644 index 0000000..5858307 --- /dev/null +++ b/apps/web-antd/src/views/_core/profile/components/secure-setting.vue @@ -0,0 +1,106 @@ + + + diff --git a/apps/web-antd/src/views/_core/profile/index.vue b/apps/web-antd/src/views/_core/profile/index.vue new file mode 100644 index 0000000..a3af56c --- /dev/null +++ b/apps/web-antd/src/views/_core/profile/index.vue @@ -0,0 +1,54 @@ + + + diff --git a/apps/web-antd/src/views/_core/profile/mitt.ts b/apps/web-antd/src/views/_core/profile/mitt.ts new file mode 100644 index 0000000..9499b91 --- /dev/null +++ b/apps/web-antd/src/views/_core/profile/mitt.ts @@ -0,0 +1,7 @@ +import { mitt } from '@vben/utils'; + +type Events = { + updateProfile: void; +}; + +export const emitter = mitt(); diff --git a/apps/web-antd/src/views/_core/profile/profile-panel.vue b/apps/web-antd/src/views/_core/profile/profile-panel.vue new file mode 100644 index 0000000..6501d0a --- /dev/null +++ b/apps/web-antd/src/views/_core/profile/profile-panel.vue @@ -0,0 +1,84 @@ + + + diff --git a/apps/web-antd/src/views/_core/profile/setting-panel.vue b/apps/web-antd/src/views/_core/profile/setting-panel.vue new file mode 100644 index 0000000..4fb8085 --- /dev/null +++ b/apps/web-antd/src/views/_core/profile/setting-panel.vue @@ -0,0 +1,59 @@ + + + diff --git a/apps/web-antd/src/views/_core/social-callback/index.vue b/apps/web-antd/src/views/_core/social-callback/index.vue new file mode 100644 index 0000000..557d268 --- /dev/null +++ b/apps/web-antd/src/views/_core/social-callback/index.vue @@ -0,0 +1,83 @@ + + + + + diff --git a/apps/web-antd/src/views/admin/FormProcessDesign.vue b/apps/web-antd/src/views/admin/FormProcessDesign.vue new file mode 100644 index 0000000..9920d93 --- /dev/null +++ b/apps/web-antd/src/views/admin/FormProcessDesign.vue @@ -0,0 +1,502 @@ + + + + + + diff --git a/apps/web-antd/src/views/admin/GroupForms.vue b/apps/web-antd/src/views/admin/GroupForms.vue new file mode 100644 index 0000000..c9af978 --- /dev/null +++ b/apps/web-antd/src/views/admin/GroupForms.vue @@ -0,0 +1,259 @@ + + + + + + diff --git a/apps/web-antd/src/views/admin/LayoutHeader.vue b/apps/web-antd/src/views/admin/LayoutHeader.vue new file mode 100644 index 0000000..18702c9 --- /dev/null +++ b/apps/web-antd/src/views/admin/LayoutHeader.vue @@ -0,0 +1,162 @@ + + + + + diff --git a/apps/web-antd/src/views/admin/index.vue b/apps/web-antd/src/views/admin/index.vue new file mode 100644 index 0000000..3fbb356 --- /dev/null +++ b/apps/web-antd/src/views/admin/index.vue @@ -0,0 +1,266 @@ + + + + + + diff --git a/apps/web-antd/src/views/admin/layout/FormBaseSetting.vue b/apps/web-antd/src/views/admin/layout/FormBaseSetting.vue new file mode 100644 index 0000000..7db013f --- /dev/null +++ b/apps/web-antd/src/views/admin/layout/FormBaseSetting.vue @@ -0,0 +1,231 @@ + + + + + diff --git a/apps/web-antd/src/views/admin/layout/FormDesign.vue b/apps/web-antd/src/views/admin/layout/FormDesign.vue new file mode 100644 index 0000000..68ed7f8 --- /dev/null +++ b/apps/web-antd/src/views/admin/layout/FormDesign.vue @@ -0,0 +1,653 @@ + + + + + + diff --git a/apps/web-antd/src/views/admin/layout/FormDesignMobilePreview.vue b/apps/web-antd/src/views/admin/layout/FormDesignMobilePreview.vue new file mode 100644 index 0000000..5b255b4 --- /dev/null +++ b/apps/web-antd/src/views/admin/layout/FormDesignMobilePreview.vue @@ -0,0 +1,92 @@ + + + + + diff --git a/apps/web-antd/src/views/admin/layout/FormProSetting.vue b/apps/web-antd/src/views/admin/layout/FormProSetting.vue new file mode 100644 index 0000000..bcf74c9 --- /dev/null +++ b/apps/web-antd/src/views/admin/layout/FormProSetting.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/apps/web-antd/src/views/admin/layout/IconSelect.vue b/apps/web-antd/src/views/admin/layout/IconSelect.vue new file mode 100644 index 0000000..812ae32 --- /dev/null +++ b/apps/web-antd/src/views/admin/layout/IconSelect.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/apps/web-antd/src/views/admin/layout/ProcessDesign.vue b/apps/web-antd/src/views/admin/layout/ProcessDesign.vue new file mode 100644 index 0000000..065b00b --- /dev/null +++ b/apps/web-antd/src/views/admin/layout/ProcessDesign.vue @@ -0,0 +1,240 @@ + + + + + diff --git a/apps/web-antd/src/views/admin/layout/action/ActionConfig.vue b/apps/web-antd/src/views/admin/layout/action/ActionConfig.vue new file mode 100644 index 0000000..d73bade --- /dev/null +++ b/apps/web-antd/src/views/admin/layout/action/ActionConfig.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/apps/web-antd/src/views/admin/layout/form/FormDesignRender.vue b/apps/web-antd/src/views/admin/layout/form/FormDesignRender.vue new file mode 100644 index 0000000..e47bf64 --- /dev/null +++ b/apps/web-antd/src/views/admin/layout/form/FormDesignRender.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/apps/web-antd/src/views/admin/layout/print/PrintTemplateDesign.vue b/apps/web-antd/src/views/admin/layout/print/PrintTemplateDesign.vue new file mode 100644 index 0000000..bd2f4ce --- /dev/null +++ b/apps/web-antd/src/views/admin/layout/print/PrintTemplateDesign.vue @@ -0,0 +1,188 @@ + + + + + diff --git a/apps/web-antd/src/views/admin/layout/process/DefaultNodeProps.js b/apps/web-antd/src/views/admin/layout/process/DefaultNodeProps.js new file mode 100644 index 0000000..0bf5dfb --- /dev/null +++ b/apps/web-antd/src/views/admin/layout/process/DefaultNodeProps.js @@ -0,0 +1,218 @@ +//审批节点默认属性 +export const APPROVAL_PROPS = { + assignedType: "ASSIGN_USER", + mode: "AND", + sign: false, + nobody: { + handler: "TO_PASS", + assignedUser: [] + }, + timeLimit: { + timeout: { + unit: "H", + value: 0 + }, + handler: { + type: "REFUSE", + notify: { + once: true, + hour: 1 + } + } + }, + assignedDept:[], + deptProp: { //指定部门/表单内部门子设置 + type: 'LEADER', //ALL=全部人员 / LEADER=主管 / ROLE=部门下的角色 + roles: [] //设置值 + }, + assignedUser: [], + formPerms: [], + selfSelect: { + multiple: false + }, + leaderTop: { + endCondition: "TOP", + endLevel: 0, + skipEmpty: false //当前级别部门未设置主管时,是否跳过 + }, + leader: { + level: 1, + skipEmpty: true //当前级别部门未设置主管时,是否跳过再寻找父级 + }, + role: [], + refuse: { + type: 'TO_END', //驳回规则 TO_END TO_NODE TO_BEFORE + target: '' //驳回到指定ID的节点 + }, + operationPerm: { + agree: {alisa: '同意', show: true}, + refuse: {alisa: '拒绝', show: true}, + transfer: {alisa: '转交', show: true}, + afterAdd: {alisa: '后加签', show: true}, + recall: {alisa: '退回', show: true}, + }, + listeners: {}, + assignedNode: [], //指定审批人的节点ID + formUser: '', //表单内人员组件ID + formDept: '', //表单内部门组件ID +} + +//办理节点配置属性 +export const TASK_PROPS = { + assignedType: "ASSIGN_USER", + mode: "AND", + nobody: { + handler: "TO_PASS", + assignedUser: [] + }, + timeLimit: { + timeout: { + unit: "H", + value: 0 + }, + handler: { + type: "PASS", + notify: { + once: true, + hour: 1 + } + } + }, + assignedDept:[], + deptProp: { + type: 'LEADER', //ALL=全部人员 / LEADER=主管 / ROLE=部门下的角色 + roles: [] //设置值 + }, + assignedUser: [], + formPerms: [], + selfSelect: { + multiple: false + }, + leaderTop: { + endCondition: "TOP", + endLevel: 0, + skipEmpty: false //当前级别部门未设置主管时,是否跳过 + }, + leader: { + level: 1, + skipEmpty: true //当前级别部门未设置主管时,是否跳过再寻找父级 + }, + role: [], + operationPerm: { + complete: {alisa: '提交', show: true}, + transfer: {alisa: '转办', show: true}, + recall: {alisa: '退回', show: true}, + }, + listeners: {}, + assignedNode: [], //指定审批人的节点ID + formUser: '', //表单内人员组件ID + formDept: '', //表单内部门组件ID +} + +//根节点默认属性 +export const ROOT_PROPS = { + assignedUser: [], + formPerms: [], + listeners: {}, + operationPerm: { + complete: {alisa: '提交', show: true} + } +} + +//条件节点默认属性 +export const CONDITION_PROPS = { + mode: 'SIMPLE', + groupsType: "OR", //条件组逻辑关系 OR、AND + groups: [ + { + groupType: "AND", //条件组内条件关系 OR、AND + cids: [], //条件ID集合 + conditions: [] //组内子条件 + } + ], + expression: "", //自定义表达式,灵活构建逻辑关系 + http: { + method: 'GET', + url: '', + contentType: 'form', + params: [ + {name: '', value: ''} + ], + headers: [ + {name: '', value: ''} + ], + pre: 'function preHandler(ctx){\n\t//发起请求前做一些处理\n\t\n}', + aft: 'function aftHandler(rsp){\n\t//获取到结果后做一些处理\n\t\n}' + } +} + +//抄送节点默认属性 +export const CC_PROPS = { + shouldAdd: false, + assignedUser: [], + formPerms: [] +} + +//触发器节点默认属性 +export const TRIGGER_PROPS = { + type: 'WEBHOOK', + http: { + method: 'GET', //请求方法 支持GET/POST + url: '', //URL地址,可以直接带参数 + headers: [ //http header + { + name: '', + isField: true, + value: '' //支持表达式 ${xxx} xxx为表单字段名称 + } + ], + contentType: 'FORM', //请求参数类型 + params: [ //请求参数 + { + name: '', + isField: true, //是表单字段还是自定义 + value: '' //支持表达式 ${xxx} xxx为表单字段名称 + } + ], + retry: 1, + handlerByScript: false, + success: 'function handlerOk(res) {\n return true;\n}', + fail: 'function handlerFail(res) {\n return true;\n}' + }, + email: { + subject: '', + to: [], + content: '' + } +} + +//延时节点默认属性 +export const DELAY_PROPS = { + type: "FIXED", //延时类型 FIXED:到达当前节点后延时固定时长 、AUTO:延时到 dateTime设置的时间 + time: 0, //延时时间 + unit: "M", //时间单位 D天 H小时 M分钟 + dateTime: "" //如果当天没有超过设置的此时间点,就延时到这个指定的时间,到了就直接跳过不延时 +} + +//子流程节点 +export const SUB_PROC_PROPS = { + subProcCode: null, //子流程编号,流程定义Id + formPerms: [], + staterUser: { //子流程发起人 + type: 'ROOT', //ROOT=与主流程一致 / CUSTOM=自选一个人 + value: null //发起人 + }, + startDept: null, //发起d部门ID + syncVersion: false, //是否每次调用子流程的最新版本 + subAll: false, //是否传递所有变量 + inVar: [ //主->子 + {mKey: null, sKey: null} + ], + outVar: [ //子 -> 主 + {mKey: null, sKey: null} + ] +} + +export default { + APPROVAL_PROPS, TASK_PROPS, CC_PROPS, DELAY_PROPS, CONDITION_PROPS, ROOT_PROPS, TRIGGER_PROPS, SUB_PROC_PROPS +} diff --git a/apps/web-antd/src/views/admin/layout/process/ProcessTree.vue b/apps/web-antd/src/views/admin/layout/process/ProcessTree.vue new file mode 100644 index 0000000..20e38b6 --- /dev/null +++ b/apps/web-antd/src/views/admin/layout/process/ProcessTree.vue @@ -0,0 +1,618 @@ + + + diff --git a/apps/web-antd/src/views/biz/guide/data.ts b/apps/web-antd/src/views/biz/guide/data.ts new file mode 100644 index 0000000..3b91e68 --- /dev/null +++ b/apps/web-antd/src/views/biz/guide/data.ts @@ -0,0 +1,185 @@ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +import { getDictOptions } from '#/utils/dict'; +import { renderDict } from '#/utils/render'; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Select', + componentProps: { + // 可选从DictEnum中获取 DictEnum.BIZ_GUIDE_CATE 便于维护 + options: getDictOptions('biz_guide_cate'), + }, + fieldName: 'category', + label: '指南类别', + }, + { + component: 'Input', + fieldName: 'categoryNo', + label: '类别代码', + }, + { + component: 'Input', + fieldName: 'title', + label: '标题', + }, + { + component: 'Input', + fieldName: 'content', + label: '内容', + }, + { + component: 'Input', + fieldName: 'file', + label: '附件模板', + }, + { + component: 'RadioGroup', + componentProps: { + // 可选从DictEnum中获取 DictEnum.SYS_DATA_STATUS 便于维护 + options: getDictOptions('sys_data_status'), + buttonStyle: 'solid', + optionType: 'button', + }, + fieldName: 'status', + label: '状态', + }, +]; + +// 需要使用i18n注意这里要改成getter形式 否则切换语言不会刷新 +// export const columns: () => VxeGridProps['columns'] = () => [ +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + // { + // title: 'ID', + // field: 'id', + // }, + { + title: '指南类别', + field: 'category', + slots: { + default: ({ row }) => { + // 可选从DictEnum中获取 DictEnum.BIZ_GUIDE_CATE 便于维护 + return row.category?renderDict(row.category, 'biz_guide_cate'):''; + }, + }, + }, + { + title: '类别代码', + field: 'categoryNo', + }, + { + title: '标题', + field: 'title', + }, + { + title: '内容', + field: 'content', + }, + { + title: '附件模板', + field: 'file', + }, + { + title: '状态', + field: 'status', + slots: { + default: ({ row }) => { + // 可选从DictEnum中获取 DictEnum.SYS_DATA_STATUS 便于维护 + return row.status?renderDict(row.status, 'sys_data_status'):''; + }, + }, + }, + { + title: '备注', + field: 'remark', + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 180, + }, +]; + +export const modalSchema: FormSchemaGetter = () => [ + { + label: 'ID', + fieldName: 'id', + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + }, + { + label: '指南类别', + fieldName: 'category', + component: 'Select', + componentProps: { + // 可选从DictEnum中获取 DictEnum.BIZ_GUIDE_CATE 便于维护 + options: getDictOptions('biz_guide_cate'), + }, + rules: 'selectRequired', + }, + { + label: '类别代码', + fieldName: 'categoryNo', + component: 'Input', + rules: 'required', + }, + { + label: '标题', + fieldName: 'title', + component: 'Input', + rules: 'required', + }, + { + label: '内容', + fieldName: 'content', + component: 'RichTextarea', + componentProps: { + // options: { + // readyonly: false, // 是否只读 + // } + // width: '100%', // 宽度 + // showImageUpload: true, // 是否显示图片上传 + // height: 400 // 高度 默认400 + }, + rules: 'required', + }, + { + label: '附件模板', + fieldName: 'file', + component: 'FileUpload', + /** + * 注意这里获取为数组 需要自行定义回显/提交 + * 文件上传还在demo阶段 可能有重大改动! + */ + componentProps: { + // accept: ['xlsx'], // 不支持type/*的写法 建议使用拓展名(不带.) + maxNumber: 5, // 最大上传文件数 + resultField: 'ossId', // 上传成功后返回的字段名 默认url 可选['ossId', 'url', 'fileName'] + }, + }, + { + label: '状态', + fieldName: 'status', + component: 'RadioGroup', + componentProps: { + // 可选从DictEnum中获取 DictEnum.SYS_DATA_STATUS 便于维护 + options: getDictOptions('sys_data_status'), + buttonStyle: 'solid', + optionType: 'button', + }, + defaultValue: '1', + rules: 'selectRequired', + }, + { + label: '备注', + fieldName: 'remark', + component: 'Textarea', + }, +]; diff --git a/apps/web-antd/src/views/biz/guide/guide-modal.vue b/apps/web-antd/src/views/biz/guide/guide-modal.vue new file mode 100644 index 0000000..931ae32 --- /dev/null +++ b/apps/web-antd/src/views/biz/guide/guide-modal.vue @@ -0,0 +1,95 @@ + + + + diff --git a/apps/web-antd/src/views/biz/guide/index.vue b/apps/web-antd/src/views/biz/guide/index.vue new file mode 100644 index 0000000..86e4a90 --- /dev/null +++ b/apps/web-antd/src/views/biz/guide/index.vue @@ -0,0 +1,186 @@ + + + diff --git a/apps/web-antd/src/views/biz/policeCapacity/data.ts b/apps/web-antd/src/views/biz/policeCapacity/data.ts new file mode 100644 index 0000000..3b9833e --- /dev/null +++ b/apps/web-antd/src/views/biz/policeCapacity/data.ts @@ -0,0 +1,258 @@ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +import { getDictOptions } from '#/utils/dict'; +import { renderDict } from '#/utils/render'; +import { getDeptTree } from '#/api/system/user'; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'name', + label: '能力名称', + }, + { + component: 'Select', + componentProps: { + // 可选从DictEnum中获取 DictEnum.BIZ_POLICE_CAPACITY_TYPE 便于维护 + options: getDictOptions('biz_police_capacity_type'), + }, + fieldName: 'policeType', + label: '警种类型', + }, +// { +// component: 'Input', +// fieldName: 'tag', +// label: '能力标签', +// }, + { +    component: 'ApiTreeSelect', +    componentProps: { +      api: getDeptTree, +      fieldNames: { label: 'label', value: 'id', children: 'children' }, +      treeLine: { showLeafIcon: false }, +      // 默认展开的节点 +      // treeDefaultExpandedKeys: [340100000000], +      showSearch: true, +      treeNodeFilterProp: 'label', +    }, +    fieldName: 'deptId', +    label: '责任单位', + }, + { + component: 'Select', + componentProps: { + // 可选从DictEnum中获取 DictEnum.BIZ_POLICE_CAPACITY_LEVEL 便于维护 + options: getDictOptions('biz_police_capacity_level'), + }, + fieldName: 'level', + label: '能力等级', + }, + // { + // component: 'RadioGroup', + // componentProps: { + // // 可选从DictEnum中获取 DictEnum.sys_yes_no_new 便于维护 + // options: getDictOptions('sys_yes_no_new'), + // buttonStyle: 'solid', + // optionType: 'button', + // }, + // fieldName: 'needApproval', + // label: '需要审批', + // }, + + // { + // component: 'RadioGroup', + // componentProps: { + // // 可选从DictEnum中获取 DictEnum.SYS_DATA_STATUS 便于维护 + // options: getDictOptions('sys_data_status'), + // buttonStyle: 'solid', + // optionType: 'button', + // }, + // fieldName: 'status', + // label: '状态', + // }, +]; + +// 需要使用i18n注意这里要改成getter形式 否则切换语言不会刷新 +// export const columns: () => VxeGridProps['columns'] = () => [ +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + // { + // title: 'ID', + // field: 'id', + // }, + { + title: '能力名称', + field: 'name', + align: 'left', + }, + { + title: '警种类型', + field: 'policeType', + slots: { + default: ({ row }) => { + // 可选从DictEnum中获取 DictEnum.BIZ_POLICE_CAPACITY_TYPE 便于维护 + return row.policeType?renderDict(row.policeType, 'biz_police_capacity_type'):''; + }, + }, + width: 100 + }, + { + title: '责任单位', + field: 'deptName', + }, + { + title: '能力等级', + field: 'level', + slots: { + default: ({ row }) => { + // 可选从DictEnum中获取 DictEnum.BIZ_POLICE_CAPACITY_LEVEL 便于维护 + return row.level?renderDict(row.level, 'biz_police_capacity_level'):''; + }, + }, + width: 100 + }, + { + title: '能力标签', + field: 'tag', + }, + { + title: '需要审批', + field: 'needApproval', + slots: { + default: ({ row }) => { + // 可选从DictEnum中获取 DictEnum.sys_yes_no_new 便于维护 + return row.needApproval?renderDict(row.needApproval, 'sys_yes_no_new'):''; + }, + }, + width: 100 + }, + // { + // title: '备注', + // field: 'remark', + // }, + // { + // title: '附件', + // field: 'ossId', + // }, + { + title: '状态', + field: 'status', + slots: { + default: ({ row }) => { + // 可选从DictEnum中获取 DictEnum.SYS_DATA_STATUS 便于维护 + return row.status?renderDict(row.status, 'sys_data_status'):''; + }, + }, + width: 100 + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 180, + }, +]; + +export const modalSchema: FormSchemaGetter = () => [ + { + label: 'ID', + fieldName: 'id', + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + }, + { + label: '警种类型', + fieldName: 'policeType', + component: 'Select', + componentProps: { + // 可选从DictEnum中获取 DictEnum.BIZ_POLICE_CAPACITY_TYPE 便于维护 + options: getDictOptions('biz_police_capacity_type'), + }, + rules: 'selectRequired', + }, + { + label: '能力名称', + fieldName: 'name', + component: 'Input', + rules: 'required', + }, + { + label: '能力等级', + fieldName: 'level', + component: 'Select', + componentProps: { + // 可选从DictEnum中获取 DictEnum.BIZ_POLICE_CAPACITY_LEVEL 便于维护 + options: getDictOptions('biz_police_capacity_level'), + }, + rules: 'selectRequired', + }, + { + label: '能力标签', + fieldName: 'tag', + component: 'Input', + }, + { +    component: 'ApiTreeSelect', +    componentProps: { +      api: getDeptTree, +      fieldNames: { label: 'label', value: 'id', children: 'children' }, +      treeLine: { showLeafIcon: false }, +      // 默认展开的节点 +      // treeDefaultExpandedKeys: [340100000000], +      showSearch: true, +      treeNodeFilterProp: 'label', +    }, +    fieldName: 'deptId', +    label: '责任单位', + }, + { + label: '需要审批', + fieldName: 'needApproval', + component: 'RadioGroup', + componentProps: { + // 可选从DictEnum中获取 DictEnum.sys_yes_no_new 便于维护 + options: getDictOptions('sys_yes_no_new'), + buttonStyle: 'solid', + optionType: 'button', + }, + defaultValue: '0', + rules: 'selectRequired', + }, + { + label: '备注', + fieldName: 'remark', + component: 'Textarea', + }, + { + label: '附件', + fieldName: 'ossId', + component: 'FileUpload', + /** + * 注意这里获取为数组 需要自行定义回显/提交 + * 文件上传还在demo阶段 可能有重大改动! + */ + componentProps: { + // accept: ['xlsx'], // 不支持type/*的写法 建议使用拓展名(不带.) + maxNumber: 5, // 最大上传文件数 + resultField: 'ossId', // 上传成功后返回的字段名 默认url 可选['ossId', 'url', 'fileName'] + }, + }, + { + label: '状态', + fieldName: 'status', + component: 'RadioGroup', + componentProps: { + // 可选从DictEnum中获取 DictEnum.SYS_DATA_STATUS 便于维护 + options: getDictOptions('sys_data_status'), + buttonStyle: 'solid', + optionType: 'button', + }, + defaultValue: '1', + rules: 'selectRequired', + }, +]; diff --git a/apps/web-antd/src/views/biz/policeCapacity/dept-tree.vue b/apps/web-antd/src/views/biz/policeCapacity/dept-tree.vue new file mode 100644 index 0000000..4cc741a --- /dev/null +++ b/apps/web-antd/src/views/biz/policeCapacity/dept-tree.vue @@ -0,0 +1,199 @@ + + + + + diff --git a/apps/web-antd/src/views/biz/policeCapacity/index.vue b/apps/web-antd/src/views/biz/policeCapacity/index.vue new file mode 100644 index 0000000..13e00e4 --- /dev/null +++ b/apps/web-antd/src/views/biz/policeCapacity/index.vue @@ -0,0 +1,188 @@ + + + diff --git a/apps/web-antd/src/views/biz/policeCapacity/policeCapacity-modal.vue b/apps/web-antd/src/views/biz/policeCapacity/policeCapacity-modal.vue new file mode 100644 index 0000000..b16e000 --- /dev/null +++ b/apps/web-antd/src/views/biz/policeCapacity/policeCapacity-modal.vue @@ -0,0 +1,116 @@ + + + + diff --git a/apps/web-antd/src/views/biz/statDept/data.ts b/apps/web-antd/src/views/biz/statDept/data.ts new file mode 100644 index 0000000..f6e02a4 --- /dev/null +++ b/apps/web-antd/src/views/biz/statDept/data.ts @@ -0,0 +1,225 @@ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +export const querySchema: FormSchemaGetter = () => [ + /* { + component: 'Input', + fieldName: 'deptId', + label: '部门ID', + }, */ + /* { + component: 'Input', + fieldName: 'deptName', + label: '部门名称', + }, */ + { + component: 'RangePicker', + fieldName: 'tjrq', + label: '统计日期', + }, + /* { + component: 'Input', + fieldName: 'rwzs', + label: '发起任务总数', + }, + { + component: 'Input', + fieldName: 'jqrws', + label: '警情任务数', + }, + { + component: 'Input', + fieldName: 'ajrws', + label: '案件任务数', + }, + { + component: 'Input', + fieldName: 'zxrws', + label: '专项任务数', + }, + { + component: 'Input', + fieldName: 'qtrws', + label: '其他任务数', + }, + { + component: 'Input', + fieldName: 'ptrws', + label: '普通任务数', + }, + { + component: 'Input', + fieldName: 'jjrws', + label: '指令任务数', + }, + { + component: 'Input', + fieldName: 'zlrws', + label: '线索核查数', + }, */ +]; + +// 需要使用i18n注意这里要改成getter形式 否则切换语言不会刷新 +// export const columns: () => VxeGridProps['columns'] = () => [ +export const columns: VxeGridProps['columns'] = [ + // { type: 'checkbox', width: 60 }, + { + title: 'ID', + field: 'id', + visible: false, // 隐藏列 + }, + { + title: '部门ID', + field: 'deptId', + visible: false, // 隐藏列 + }, + { + title: '部门名称', + field: 'deptName', + minWidth: 100, + }, + { + title: '任务类型', + children: [ + { + title: '发起任务总数', + field: 'rwzs', + }, + { + title: '警情任务数', + field: 'jqrws', + }, + { + title: '案件任务数', + field: 'ajrws', + }, + { + title: '专项任务数', + field: 'zxrws', + }, + { + title: '其他任务数', + field: 'qtrws', + }, + ], + }, + { + title: '任务级别', + children: [ + { + title: '普通任务数', + field: 'ptrws', + }, + { + title: '指令任务数', + field: 'jjrws', + }, + { + title: '敏感任务数', + field: 'mgrws', + }, + { + title: '情指任务数', + field: 'qzrws', + }, + { + title: '线索核查数', + field: 'zlrws', + }, + { + title: '特殊任务数', + field: 'tsrws', + }, + ], + }, + /* { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 180, + }, */ +]; + +export const modalSchema: FormSchemaGetter = () => [ + { + label: 'ID', + fieldName: 'id', + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + }, + { + label: '部门ID', + fieldName: 'deptId', + component: 'Input', + rules: 'required', + }, + { + label: '部门名称', + fieldName: 'deptName', + component: 'Input', + rules: 'required', + }, + { + label: '统计日期', + fieldName: 'tjrq', + component: 'DatePicker', + componentProps: { + showTime: true, + format: 'YYYY-MM-DD', + valueFormat: 'YYYY-MM-DD', + }, + rules: 'required', + }, + { + label: '发起任务总数', + fieldName: 'rwzs', + component: 'Input', + rules: 'required', + }, + { + label: '警情任务数', + fieldName: 'jqrws', + component: 'Input', + rules: 'required', + }, + { + label: '案件任务数', + fieldName: 'ajrws', + component: 'Input', + rules: 'required', + }, + { + label: '专项任务数', + fieldName: 'zxrws', + component: 'Input', + rules: 'required', + }, + { + label: '其他任务数', + fieldName: 'qtrws', + component: 'Input', + rules: 'required', + }, + { + label: '普通任务数', + fieldName: 'ptrws', + component: 'Input', + rules: 'required', + }, + { + label: '指令任务数', + fieldName: 'jjrws', + component: 'Input', + rules: 'required', + }, + { + label: '线索核查数', + fieldName: 'zlrws', + component: 'Input', + rules: 'required', + }, +]; diff --git a/apps/web-antd/src/views/biz/statDept/dept-tree.vue b/apps/web-antd/src/views/biz/statDept/dept-tree.vue new file mode 100644 index 0000000..fb3b40e --- /dev/null +++ b/apps/web-antd/src/views/biz/statDept/dept-tree.vue @@ -0,0 +1,211 @@ + + + + + diff --git a/apps/web-antd/src/views/biz/statDept/index.vue b/apps/web-antd/src/views/biz/statDept/index.vue new file mode 100644 index 0000000..17dcdc2 --- /dev/null +++ b/apps/web-antd/src/views/biz/statDept/index.vue @@ -0,0 +1,217 @@ + + + diff --git a/apps/web-antd/src/views/biz/statDept/statDept-modal.vue b/apps/web-antd/src/views/biz/statDept/statDept-modal.vue new file mode 100644 index 0000000..bdbcff9 --- /dev/null +++ b/apps/web-antd/src/views/biz/statDept/statDept-modal.vue @@ -0,0 +1,98 @@ + + + + diff --git a/apps/web-antd/src/views/chat/index.vue b/apps/web-antd/src/views/chat/index.vue new file mode 100644 index 0000000..1eaa23b --- /dev/null +++ b/apps/web-antd/src/views/chat/index.vue @@ -0,0 +1,94 @@ + + + + + + diff --git a/apps/web-antd/src/views/common/InsertButton.vue b/apps/web-antd/src/views/common/InsertButton.vue new file mode 100644 index 0000000..21d71d7 --- /dev/null +++ b/apps/web-antd/src/views/common/InsertButton.vue @@ -0,0 +1,126 @@ + + + + + diff --git a/apps/web-antd/src/views/common/InstanceSearch.vue b/apps/web-antd/src/views/common/InstanceSearch.vue new file mode 100644 index 0000000..62f22bc --- /dev/null +++ b/apps/web-antd/src/views/common/InstanceSearch.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/ChangeHis.vue b/apps/web-antd/src/views/common/form/ChangeHis.vue new file mode 100644 index 0000000..30a68a7 --- /dev/null +++ b/apps/web-antd/src/views/common/form/ChangeHis.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/ComponentExport.js b/apps/web-antd/src/views/common/form/ComponentExport.js new file mode 100644 index 0000000..b57e23c --- /dev/null +++ b/apps/web-antd/src/views/common/form/ComponentExport.js @@ -0,0 +1,117 @@ +import * as Vue from 'vue' +let TextInput = Vue.defineAsyncComponent( + () => import('./components/TextInput.vue') +) +let NumberInput = Vue.defineAsyncComponent( + () => import('./components/NumberInput.vue') +) +let AmountInput = Vue.defineAsyncComponent( + () => import('./components/AmountInput.vue') +) +let TextareaInput = Vue.defineAsyncComponent( + () => import('./components/TextareaInput.vue') +) +let SelectInput = Vue.defineAsyncComponent( + () => import('./components/SelectInput.vue') +) +let MultipleSelect = Vue.defineAsyncComponent( + () => import('./components/MultipleSelect.vue') +) +let DateTime = Vue.defineAsyncComponent( + () => import('./components/DateTime.vue') +) +let DateTimeRange = Vue.defineAsyncComponent( + () => import('./components/DateTimeRange.vue') +) +let TimePicker = Vue.defineAsyncComponent( + () => import('./components/TimePicker.vue') +) +let TimeRangePicker = Vue.defineAsyncComponent( + () => import('./components/TimeRangePicker.vue') +) +let Description = Vue.defineAsyncComponent( + () => import('./components/Description.vue') +) +let ImageUpload = Vue.defineAsyncComponent( + () => import('./components/ImageUpload.vue') +) +let FileUpload = Vue.defineAsyncComponent( + () => import('./components/FileUpload.vue') +) +let MoneyInput = Vue.defineAsyncComponent( + () => import('./components/MoneyInput.vue') +) +let DeptPicker = Vue.defineAsyncComponent( + () => import('./components/DeptPicker.vue') +) +let UserPicker = Vue.defineAsyncComponent( + () => import('./components/UserPicker.vue') +) +let SignPanel = Vue.defineAsyncComponent( + () => import('./components/SignPanel.vue') +) +let Score = Vue.defineAsyncComponent(() => import('./components/Score.vue')) + +let SpanLayout = Vue.defineAsyncComponent( + () => import('./components/SpanLayout.vue') +) +let TableList = Vue.defineAsyncComponent( + () => import('./components/TableList.vue') +) +let Location = Vue.defineAsyncComponent( + () => import('./components/Location.vue') +) +let Provinces = Vue.defineAsyncComponent( + () => import('./components/Provinces.vue') +) +let WebIframe = Vue.defineAsyncComponent( + () => import('./components/WebIframe.vue') +) +let CalcFormula = Vue.defineAsyncComponent( + () => import('./components/CalcFormula.vue') +) +let ProcessIndex = Vue.defineAsyncComponent( + () => import('./components/ProcessIndex.vue') +) +let VueContainer = Vue.defineAsyncComponent( + () => import('./components/VueContainer.vue') +) +let OuterForm = Vue.defineAsyncComponent( + () => import('./components/OuterForm.vue') +) +let SelectPlus = Vue.defineAsyncComponent( + () => import('./components/SelectPlus.vue') +) + +export default { + //基础组件 + TextInput, + NumberInput, + AmountInput, + TextareaInput, + SelectInput, + MultipleSelect, + DateTime, + DateTimeRange, + UserPicker, + DeptPicker, + TimePicker, + TimeRangePicker, + //高级组件 + Description, + FileUpload, + ImageUpload, + MoneyInput, + Location, + SignPanel, + CalcFormula, + SpanLayout, + TableList, + Provinces, + Score, + WebIframe, + VueContainer, + ProcessIndex, + OuterForm, + SelectPlus +} diff --git a/apps/web-antd/src/views/common/form/ComponentMinxins.js b/apps/web-antd/src/views/common/form/ComponentMinxins.js new file mode 100644 index 0000000..0d79d8d --- /dev/null +++ b/apps/web-antd/src/views/common/form/ComponentMinxins.js @@ -0,0 +1,31 @@ + +export default { + props: { + mode: { + type: String, + default: 'DESIGN', + }, + required: { + type: Boolean, + default: false, + }, + readonly: { + type: Boolean, + default: false, + }, + }, + data() { + return {} + }, + computed: { + _value: { + get() { + return this.modelValue + }, + set(val) { + this.$emit('update:modelValue', val) + }, + }, + }, + emits: ['update:modelValue'], +} diff --git a/apps/web-antd/src/views/common/form/ComponentsConfigExport.js b/apps/web-antd/src/views/common/form/ComponentsConfigExport.js new file mode 100644 index 0000000..f13b8c4 --- /dev/null +++ b/apps/web-antd/src/views/common/form/ComponentsConfigExport.js @@ -0,0 +1,428 @@ +export const ValueType = { + string: 'String', + object: 'Object', + array: 'Array', + number: 'Number', + date: 'Date', + user: 'User', + dept: 'Dept', + dateRange: 'DateRange', + getValidType: (type) => { + switch (type) { + case 'Dept': + case 'User': + case 'DateRange': + return 'array' + case 'Array': + case 'Number': + case 'String': + return type.toLowerCase() + default: + return undefined + } + } +} + +export const baseComponents = [ + { + name: '布局', + components: [ + { + title: '分栏布局', + name: 'SpanLayout', + icon: 'tabler:layout-columns', + value: [], + valueType: ValueType.array, + props: { + items:[], + number: 2 //分几栏,均等分能被24整除 + } + } + ] + }, { + name: '基础组件', + components: [ + { + title: '单行文本输入', + name: 'TextInput', + icon: 'iconamoon:edit', + key: '', + valueType: ValueType.string, + props: { + enableScan: false, + required: false, + enablePrint: true, + abstract: false + } + }, + { + title: '多行文本输入', + name: 'TextareaInput', + icon: 'solar:list-down-line-duotone', + key: '', + valueType: ValueType.string, + props: { + enableScan: false, + required: false, + enablePrint: true, + abstract: false + } + }, + { + title: '数字输入框', + name: 'NumberInput', + icon: 'tabler:123', + key: '', + valueType: ValueType.number, + props: { + required: false, + enablePrint: true, + precision: undefined, + abstract: false + } + }, + { + title: '金额输入框', + name: 'AmountInput', + icon: 'f7:money-yen-circle', + key: '', + valueType: ValueType.number, + props: { + required: false, + enablePrint: true, + showChinese: true, + precision: undefined, + abstract: false + } + }, + { + title: '单选框', + name: 'SelectInput', + icon: 'mdi:radiobox-marked', + key: '', + valueType: ValueType.string, + props: { + required: false, + enablePrint: true, + expanding: false, + options: ['选项1', '选项2'], + abstract: false + } + }, + { + title: '多选框', + name: 'MultipleSelect', + icon: 'mingcute:multiselect-line', + key: '', + valueType: ValueType.array, + props: { + required: false, + enablePrint: true, + expanding: false, + options: ['选项1', '选项2'], + abstract: false + } + }, + { + title: '时间选择', + name: 'TimePicker', + icon: 'gridicons:time', + key: '', + valueType: ValueType.date, + props: { + required: false, + enablePrint: true, + abstract: false + } + }, + { + title: '时间范围选择', + name: 'TimeRangePicker', + icon: 'zmdi:time-interval', + key: '', + valueType: ValueType.dateRange, + props: { + required: false, + enablePrint: true, + placeholder: ['开始时间', '结束时间'], + showLength: false, + abstract: false + } + }, + { + title: '日期时间点', + name: 'DateTime', + icon: 'material-symbols:calendar-clock-outline', + key: '', + valueType: ValueType.date, + props: { + required: false, + enablePrint: true, + format: 'YYYY-MM-DD HH:mm', + abstract: false + } + }, + { + title: '日期时间区间', + name: 'DateTimeRange', + icon: 'material-symbols:calendar-month-outline', + key: '', + valueType: ValueType.dateRange, + props: { + required: false, + enablePrint: true, + placeholder: ['开始时间', '结束时间'], + format: 'YYYY-MM-DD HH:mm', + showLength: false, + abstract: false + } + }, + { + title: '上传图片', + name: 'ImageUpload', + icon: 'mingcute:pic-2-line', + key: '', + valueType: ValueType.array, + props: { + required: false, + enablePrint: true, + maxSize: 5, //图片最大大小MB + maxNumber: 10, //最大上传数量 + enableZip: true, //图片压缩后再上传 + abstract: false + } + }, + { + title: '上传附件', + name: 'FileUpload', + icon: 'material-symbols:folder-open-outline', + key: '', + valueType: ValueType.array, + props: { + required: false, + enablePrint: true, + onlyRead: false, //是否只读,false只能在线预览,true可以下载 + maxSize: 100, //文件最大大小MB + maxNumber: 10, //最大上传数量 + fileTypes: [], //限制文件上传类型 + abstract: false + } + }, + { + title: '人员选择', + name: 'UserPicker', + icon: 'gravity-ui:persons', + key: '', + valueType: ValueType.user, + props: { + required: false, + enablePrint: true, + multiple: false, + expansion: false, + options: [], + abstract: false + } + }, + { + title: '部门选择', + name: 'DeptPicker', + icon: 'fluent:organization-24-regular', + key: '', + valueType: ValueType.dept, + props: { + required: false, + enablePrint: true, + multiple: false, + expansion: false, + options: [], + abstract: false + } + },{ + title: '评分', + name: 'Score', + icon: 'fluent:star-12-regular', + key: '', + valueType: ValueType.number, + props: { + required: false, + enablePrint: true, + color: '#f0a732', + max: 5, + showScore: true, + enableHalf: false, + type: 'star', + abstract: false + } + }, + { + title: '说明文字', + name: 'Description', + icon: 'clarity:info-standard-line', + key: '', + valueType: ValueType.string, + props: { + required: false, + enablePrint: true, + color: '#818181', + abstract: false + } + }, + ] + }, { + name: '扩展组件', + components: [ + { + title: '明细表', + name: 'TableList', + icon: 'mdi:table', + key: '', + valueType: ValueType.array, + props: { + required: false, + enablePrint: true, + showBorder: true, + rowLayout: true, + showSummary: false, + summaryColumns: [], + maxSize: 0, //最大条数,为0则不限制 + columns:[], //列设置 + abstract: false + } + }, + { + title: '高级选择器', + name: 'SelectPlus', + icon: 'iconoir:open-select-hand-gesture', + key: '', + valueType: ValueType.array, + props: { + required: false, + enablePrint: true, + multiple: false, //是否多选 + expanding: false, //是否展开选项 + fixed: false, //是否是手动设置的固定项 + options: [], //固定项 + http: {}, + abstract: false + } + }, + { + title: '地理位置', + name: 'Location', + icon: 'material-symbols:location-on-outline', + key: '', + valueType: ValueType.object, + props: { + required: false, + enablePrint: true, + enableEdit: false, + abstract: false + } + }, + { + title: '省市区', + name: 'Provinces', + icon: 'material-symbols:local-see-outline', + key: '', + valueType: ValueType.string, + props: { + required: false, + enablePrint: true, + level: 3, + abstract: false + } + }, + { + title: '签名', + name: 'SignPanel', + icon: 'majesticons:edit-pen-4', + key: '', + valueType: ValueType.string, + props: { + required: false, + enablePrint: true, + thickness: 2, + color: '#000000', + abstract: false + } + }, + { + title: '流程关联', + name: 'ProcessIndex', + icon: 'solar:link-round-bold', + key: '', + valueType: ValueType.array, + props: { + required: false, + enablePrint: false, + processCode: null, + abstract: false + } + }, { + title: '计算公式', + name: 'CalcFormula', + icon: 'pajamas:formula', + key: '', + valueType: ValueType.number, + props: { + precision: 0, + required: false, + enablePrint: false, + isPlus: false, + jsCode: '', + explain: [], + abstract: false + } + }, + { + title: '外部表单', + name: 'OuterForm', + icon: 'material-symbols:sheets-add-on', + key: '', + valueType: ValueType.object, + props: { + required: false, + enablePrint: true, + isCodeForm: true, //是否是代码写的内部表单 + path: null, //表单访问路径,如果是代码写的表单就是组件地址,url表单就是url地址 + formProps: null, //表单的属性设置信息 + height: '', + abstract: false + } + }, + { + title: '网页iframe', + name: 'WebIframe', + icon: 'mingcute:chrome-line', + key: '', + valueType: ValueType.string, + props: { + required: false, + enablePrint: false, + url: '', + height: 200, + abstract: false + } + }, + { + title: 'Vue容器', + name: 'VueContainer', + icon: 'la:vuejs', + key: '', + valueType: ValueType.string, + props: { + required: false, + enablePrint: false, + template: null, + cpStyle: null, + cpJs: null, + abstract: false + } + }, + ] + } +] + +export default { + baseComponents, +} diff --git a/apps/web-antd/src/views/common/form/ConfigMinxins.js b/apps/web-antd/src/views/common/form/ConfigMinxins.js new file mode 100644 index 0000000..88cf539 --- /dev/null +++ b/apps/web-antd/src/views/common/form/ConfigMinxins.js @@ -0,0 +1,11 @@ +export default { + props: { + modelValue: { + type: Object, + default: () => { + return {} + }, + }, + }, + emits: ['update:modelValue'], +} diff --git a/apps/web-antd/src/views/common/form/FormComponentConfig.vue b/apps/web-antd/src/views/common/form/FormComponentConfig.vue new file mode 100644 index 0000000..a9a8170 --- /dev/null +++ b/apps/web-antd/src/views/common/form/FormComponentConfig.vue @@ -0,0 +1,119 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/FormRender.vue b/apps/web-antd/src/views/common/form/FormRender.vue new file mode 100644 index 0000000..fac72ff --- /dev/null +++ b/apps/web-antd/src/views/common/form/FormRender.vue @@ -0,0 +1,480 @@ + + + + + + diff --git a/apps/web-antd/src/views/common/form/OuterFormMixin.js b/apps/web-antd/src/views/common/form/OuterFormMixin.js new file mode 100644 index 0000000..d34a51b --- /dev/null +++ b/apps/web-antd/src/views/common/form/OuterFormMixin.js @@ -0,0 +1,108 @@ +//混入外部表单组件配置,所有接入wflow的外部表单都需要这个配置 +export default{ + props:{ + mode:{ + type: String, + default: 'DESIGN' + }, + required:{ + type: Boolean, + default: false + }, + perm:{ + type: String, + default: 'R' + }, + modelValue:{ + type: Object, + default: () => { + return {} + } + } + }, + data(){ + return { + _formData: null, //定义表单数据 + _formRef: '' + } + }, + computed: { + _value: { + get() { + return this.modelValue; + }, + set(val) { + this.$emit("update:modelValue", val); + this.postMsg({type: 'WFLOW_FORM_DATA_CHANGE', formData: val}) + } + }, + _disabled() { + return this.$route.query.perm !== 'E' && this.perm !== 'E' + } + }, + mounted() { + window.addEventListener('message',this.onMessage, false) + this.postMsg({type: 'WFLOW_GET_FORM_DATA'}) + }, + beforeDestroy() { + window.removeEventListener('message', this.onMessage) + }, + methods:{ + /** + * 初始化表单,外部表需要在mounted里面调用 + * @param formData 表单绑定数据对象 + * @param formRef 表单的ref name + */ + formInit(formData, formRef){ + this._formData = formData + this._formRef = formRef + //属性字段赋值初始化 + for (const key in formData) { + this._formData[key] = this._value[key] === undefined ? null : this._value[key] + } + }, + onMessage(ev){ + if (ev.source !== ev.target){ + switch (ev.data.type){ + case 'WFLOW_FORM_VALIDATE': + this.validate(valid => { + this.postMsg({ + type: 'WFLOW_FORM_VALID', + valid: valid, + formData: this._formData + }) + }) + break + case 'WFLOW_SET_FORM_DATA': + //加载表单数据 + console.log('iframe收到表单值', ev.data.formData) + if (ev.data.formData){ + for (const key of Object.keys(ev.data.formData)) { + this._formData[key] = ev.data.formData[key] + } + } + break + } + } + }, + validate(call){ + this.$refs[this._formRef].validate((valid) => { + if (call){ + call(valid) + } + }); + }, + postMsg(msg){ + window.parent.postMessage(JSON.parse(JSON.stringify(msg))) + } + }, + emits: ['update:modelValue'], + watch:{ + _formData: { + deep: true, + handler(){ + this._value = this._formData + } + } + } +} diff --git a/apps/web-antd/src/views/common/form/OuterFormReg.js b/apps/web-antd/src/views/common/form/OuterFormReg.js new file mode 100644 index 0000000..5150270 --- /dev/null +++ b/apps/web-antd/src/views/common/form/OuterFormReg.js @@ -0,0 +1,10 @@ +import {defineAsyncComponent} from "vue"; + +//需要注册的表单组件放到这里来 +const TestForm = defineAsyncComponent( + () => import('./TestForm.vue') +) + +export default { + TestForm +} diff --git a/apps/web-antd/src/views/common/form/TestForm.vue b/apps/web-antd/src/views/common/form/TestForm.vue new file mode 100644 index 0000000..7b42bdf --- /dev/null +++ b/apps/web-antd/src/views/common/form/TestForm.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/AmountInput.vue b/apps/web-antd/src/views/common/form/components/AmountInput.vue new file mode 100644 index 0000000..a1b8be0 --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/AmountInput.vue @@ -0,0 +1,200 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/CalcFormula.vue b/apps/web-antd/src/views/common/form/components/CalcFormula.vue new file mode 100644 index 0000000..cbad6fa --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/CalcFormula.vue @@ -0,0 +1,127 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/CascaderSelect.vue b/apps/web-antd/src/views/common/form/components/CascaderSelect.vue new file mode 100644 index 0000000..bd191e3 --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/CascaderSelect.vue @@ -0,0 +1,14 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/DateTime.vue b/apps/web-antd/src/views/common/form/components/DateTime.vue new file mode 100644 index 0000000..9192e8c --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/DateTime.vue @@ -0,0 +1,139 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/DateTimeRange.vue b/apps/web-antd/src/views/common/form/components/DateTimeRange.vue new file mode 100644 index 0000000..e6bdb2e --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/DateTimeRange.vue @@ -0,0 +1,196 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/DeptPicker.vue b/apps/web-antd/src/views/common/form/components/DeptPicker.vue new file mode 100644 index 0000000..e47cc05 --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/DeptPicker.vue @@ -0,0 +1,134 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/Description.vue b/apps/web-antd/src/views/common/form/components/Description.vue new file mode 100644 index 0000000..860bdb5 --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/Description.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/FileUpload.vue b/apps/web-antd/src/views/common/form/components/FileUpload.vue new file mode 100644 index 0000000..7f18123 --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/FileUpload.vue @@ -0,0 +1,274 @@ + + + + + + diff --git a/apps/web-antd/src/views/common/form/components/ImageUpload.vue b/apps/web-antd/src/views/common/form/components/ImageUpload.vue new file mode 100644 index 0000000..a97b886 --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/ImageUpload.vue @@ -0,0 +1,259 @@ + + + + + + diff --git a/apps/web-antd/src/views/common/form/components/Location.vue b/apps/web-antd/src/views/common/form/components/Location.vue new file mode 100644 index 0000000..44f01d8 --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/Location.vue @@ -0,0 +1,180 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/MoneyInput.vue b/apps/web-antd/src/views/common/form/components/MoneyInput.vue new file mode 100644 index 0000000..c91d66f --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/MoneyInput.vue @@ -0,0 +1,14 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/MultipleSelect.vue b/apps/web-antd/src/views/common/form/components/MultipleSelect.vue new file mode 100644 index 0000000..d7e27c8 --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/MultipleSelect.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/NumberInput.vue b/apps/web-antd/src/views/common/form/components/NumberInput.vue new file mode 100644 index 0000000..d78cf93 --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/NumberInput.vue @@ -0,0 +1,81 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/OrgPicker.vue b/apps/web-antd/src/views/common/form/components/OrgPicker.vue new file mode 100644 index 0000000..3ca914e --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/OrgPicker.vue @@ -0,0 +1,72 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/OuterForm.vue b/apps/web-antd/src/views/common/form/components/OuterForm.vue new file mode 100644 index 0000000..9664b5e --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/OuterForm.vue @@ -0,0 +1,178 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/ProcessIndex.vue b/apps/web-antd/src/views/common/form/components/ProcessIndex.vue new file mode 100644 index 0000000..8720601 --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/ProcessIndex.vue @@ -0,0 +1,360 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/Provinces.vue b/apps/web-antd/src/views/common/form/components/Provinces.vue new file mode 100644 index 0000000..5cda36e --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/Provinces.vue @@ -0,0 +1,145 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/Score.vue b/apps/web-antd/src/views/common/form/components/Score.vue new file mode 100644 index 0000000..f88a123 --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/Score.vue @@ -0,0 +1,72 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/SelectInput.vue b/apps/web-antd/src/views/common/form/components/SelectInput.vue new file mode 100644 index 0000000..6457c91 --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/SelectInput.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/SelectPlus.vue b/apps/web-antd/src/views/common/form/components/SelectPlus.vue new file mode 100644 index 0000000..e9e3997 --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/SelectPlus.vue @@ -0,0 +1,298 @@ + + + + + + diff --git a/apps/web-antd/src/views/common/form/components/SignPanel.vue b/apps/web-antd/src/views/common/form/components/SignPanel.vue new file mode 100644 index 0000000..b0f8405 --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/SignPanel.vue @@ -0,0 +1,157 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/SpanLayout.vue b/apps/web-antd/src/views/common/form/components/SpanLayout.vue new file mode 100644 index 0000000..acd55ae --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/SpanLayout.vue @@ -0,0 +1,304 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/TableList.vue b/apps/web-antd/src/views/common/form/components/TableList.vue new file mode 100644 index 0000000..1e1feec --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/TableList.vue @@ -0,0 +1,532 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/TextInput.vue b/apps/web-antd/src/views/common/form/components/TextInput.vue new file mode 100644 index 0000000..afb4c60 --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/TextInput.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/TextareaInput.vue b/apps/web-antd/src/views/common/form/components/TextareaInput.vue new file mode 100644 index 0000000..dee801c --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/TextareaInput.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/TimePicker.vue b/apps/web-antd/src/views/common/form/components/TimePicker.vue new file mode 100644 index 0000000..4c71687 --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/TimePicker.vue @@ -0,0 +1,87 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/TimeRangePicker.vue b/apps/web-antd/src/views/common/form/components/TimeRangePicker.vue new file mode 100644 index 0000000..959b648 --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/TimeRangePicker.vue @@ -0,0 +1,182 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/UserPicker.vue b/apps/web-antd/src/views/common/form/components/UserPicker.vue new file mode 100644 index 0000000..eb8fa49 --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/UserPicker.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/VueContainer.vue b/apps/web-antd/src/views/common/form/components/VueContainer.vue new file mode 100644 index 0000000..7211444 --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/VueContainer.vue @@ -0,0 +1,184 @@ + + + diff --git a/apps/web-antd/src/views/common/form/components/WebIframe.vue b/apps/web-antd/src/views/common/form/components/WebIframe.vue new file mode 100644 index 0000000..276757a --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/WebIframe.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/compare/CompareOptions.js b/apps/web-antd/src/views/common/form/components/compare/CompareOptions.js new file mode 100644 index 0000000..3e0f71b --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/compare/CompareOptions.js @@ -0,0 +1,101 @@ +//字符串比较选项 +import moment from "moment"; + +const strCompareOptions = [ + {name: '等于', symbol: 'EQ', compare: (a, b) => a == b[0]}, + {name: '不等于', symbol: 'NEQ', compare: (a, b) => a != b[0]}, + {name: '为其之一', symbol: 'IN', compare: (a, b) => (b || []).includes(a)}, + {name: '不为其之一', symbol: 'NIN', compare: (a, b) => !(b || []).includes(a)}, + {name: '含有', symbol: 'LIKE', compare: (a, b) => (b || '').includes(a)}, +] + +const strArrCompareOptions = [ + {name: '包含', symbol: 'HS', compare: (a, b) => checkElementsExistInArray(b, a)}, + {name: '不包含', symbol: 'NHS', compare: (a, b) => !checkElementsExistInArray(b, a)}, +] + +const numCompareOptions = [ + {name: '大于', symbol: 'GT', compare: (a, b) => a > b}, + {name: '小于', symbol: 'LT', compare: (a, b) => a < b}, + {name: '等于', symbol: 'EQ', compare: (a, b) => a == b}, + {name: '不等于', symbol: 'NEQ', compare: (a, b) => a != b}, + {name: '大于等于', symbol: 'GT_EQ', compare: (a, b) => a >= b}, + {name: '小于等于', symbol: 'LT_EQ', compare: (a, b) => a <= b}, + {name: '在内或相等', symbol: 'IN_EQ', compare: (a, b) => a >= b[0] && a <= b[1]}, + {name: '在内或不等', symbol: 'IN_NEQ', compare: (a, b) => a == b}, + {name: '为其之一', symbol: 'IN', compare: (a, b) => (b || []).includes(a)}, + {name: '不为其之一', symbol: 'NIN', compare: (a, b) => !(b || []).includes(a)}, +] + +const orgCompareOptions = [ + {name: '是', symbol: 'EQ', compare: (a, b) => a[0].id == b[0].id}, + {name: '不是', symbol: 'NEQ', compare: (a, b) => a[0].id != b[0].id}, + {name: '为其之一', symbol: 'IN', compare: (a, b) => checkElementsExistInArray(a, b, 'id')}, + {name: '含有', symbol: 'HS', compare: (a, b) => checkElementsExistInArray(b, a, 'id')}, + {name: '不含有', symbol: 'NHS', compare: (a, b) => !checkElementsExistInArray(b, a, 'id')}, +] + +const timeCompareOptions = [ + {name: '在之前', symbol: 'LT', compare: (a, b) => moment(a).isBefore(moment(b[0]))}, + {name: '在之后', symbol: 'GT', compare: (a, b) => moment(a).isAfter(moment(b[0]))}, + {name: '在其中', symbol: 'IN', compare: (a, b) => moment(b[0]).isBefore(moment(a)) && moment(b[1]).isAfter(moment(a))}, +] + +const timeArrCompareOptions = [ + {name: '包含', symbol: 'HS', compare: (a, b) => moment(a[0]).isBefore(moment(b[0])) && moment(a[1]).isAfter(moment(b[0]))}, + {name: '不包含', symbol: 'NHS', compare: (a, b) => moment(b[0]).isBefore(moment(a[0])) || moment(b[0]).isAfter(moment(a[1]))}, +] + +//加载比较函数对象 +function getCompareFucs(){ + let cpFuncs = { + strCompare:{}, + strArrCompare:{}, + numCompare:{}, + orgCompare:{}, + timeCompare:{}, + timeArrCompare:{} + } + strCompareOptions.forEach(v => cpFuncs.strCompare[v.symbol] = v.compare) + strArrCompareOptions.forEach(v => cpFuncs.strArrCompare[v.symbol] = v.compare) + numCompareOptions.forEach(v => cpFuncs.numCompare[v.symbol] = v.compare) + orgCompareOptions.forEach(v => cpFuncs.orgCompare[v.symbol] = v.compare) + timeCompareOptions.forEach(v => cpFuncs.timeCompare[v.symbol] = v.compare) + timeArrCompareOptions.forEach(v => cpFuncs.timeArrCompare[v.symbol] = v.compare) + return cpFuncs +} + +//校验数组A是否包含在B内 +export function checkElementsExistInArray(A, B, key) { + if (A.length === 0 || B.length === 0){ + return false + } + for (let i = 0; i < A.length; i++) { + let found = false; + for (let j = 0; j < B.length; j++) { + if (key){ + if (A[i][key] == B[j][key]) { + found = true; + break; + } + }else { + if (A[i] == B[j]) { + found = true; + break; + } + } + } + if (!found) { + return false; + } + } + return true; +} + +export const CompareFuncs = getCompareFucs() + +export default { + strCompareOptions, strArrCompareOptions, + numCompareOptions, orgCompareOptions, + timeCompareOptions, timeArrCompareOptions +} diff --git a/apps/web-antd/src/views/common/form/components/compare/NumCompare.vue b/apps/web-antd/src/views/common/form/components/compare/NumCompare.vue new file mode 100644 index 0000000..5c2719e --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/compare/NumCompare.vue @@ -0,0 +1,24 @@ + + diff --git a/apps/web-antd/src/views/common/form/components/compare/OrgCompare.vue b/apps/web-antd/src/views/common/form/components/compare/OrgCompare.vue new file mode 100644 index 0000000..da25dcb --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/compare/OrgCompare.vue @@ -0,0 +1,32 @@ + + diff --git a/apps/web-antd/src/views/common/form/components/compare/StrArrCompare.vue b/apps/web-antd/src/views/common/form/components/compare/StrArrCompare.vue new file mode 100644 index 0000000..426f1ed --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/compare/StrArrCompare.vue @@ -0,0 +1,15 @@ + + diff --git a/apps/web-antd/src/views/common/form/components/compare/StrCompare.vue b/apps/web-antd/src/views/common/form/components/compare/StrCompare.vue new file mode 100644 index 0000000..c635547 --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/compare/StrCompare.vue @@ -0,0 +1,20 @@ + + diff --git a/apps/web-antd/src/views/common/form/components/compare/TimeArrCompare.vue b/apps/web-antd/src/views/common/form/components/compare/TimeArrCompare.vue new file mode 100644 index 0000000..28c7c6d --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/compare/TimeArrCompare.vue @@ -0,0 +1,19 @@ + + diff --git a/apps/web-antd/src/views/common/form/components/compare/TimeCompare.vue b/apps/web-antd/src/views/common/form/components/compare/TimeCompare.vue new file mode 100644 index 0000000..a719399 --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/compare/TimeCompare.vue @@ -0,0 +1,22 @@ + + diff --git a/apps/web-antd/src/views/common/form/components/subs/EmptyForm.vue b/apps/web-antd/src/views/common/form/components/subs/EmptyForm.vue new file mode 100644 index 0000000..82b85ce --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/subs/EmptyForm.vue @@ -0,0 +1,13 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/components/subs/MProcessItem.vue b/apps/web-antd/src/views/common/form/components/subs/MProcessItem.vue new file mode 100644 index 0000000..3beaacf --- /dev/null +++ b/apps/web-antd/src/views/common/form/components/subs/MProcessItem.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/AmountInputConfig.vue b/apps/web-antd/src/views/common/form/config/AmountInputConfig.vue new file mode 100644 index 0000000..2ce8464 --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/AmountInputConfig.vue @@ -0,0 +1,25 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/CalcFormulaConfig.vue b/apps/web-antd/src/views/common/form/config/CalcFormulaConfig.vue new file mode 100644 index 0000000..e26023c --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/CalcFormulaConfig.vue @@ -0,0 +1,346 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/DateTimeConfig.vue b/apps/web-antd/src/views/common/form/config/DateTimeConfig.vue new file mode 100644 index 0000000..1d355d5 --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/DateTimeConfig.vue @@ -0,0 +1,27 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/DateTimeRangeConfig.vue b/apps/web-antd/src/views/common/form/config/DateTimeRangeConfig.vue new file mode 100644 index 0000000..c8eefa4 --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/DateTimeRangeConfig.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/DeptPickerConfig.vue b/apps/web-antd/src/views/common/form/config/DeptPickerConfig.vue new file mode 100644 index 0000000..ec8bb4b --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/DeptPickerConfig.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/DescriptionConfig.vue b/apps/web-antd/src/views/common/form/config/DescriptionConfig.vue new file mode 100644 index 0000000..611b808 --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/DescriptionConfig.vue @@ -0,0 +1,22 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/FileUploadConfig.vue b/apps/web-antd/src/views/common/form/config/FileUploadConfig.vue new file mode 100644 index 0000000..32fb186 --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/FileUploadConfig.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/FormConfig.vue b/apps/web-antd/src/views/common/form/config/FormConfig.vue new file mode 100644 index 0000000..9a65913 --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/FormConfig.vue @@ -0,0 +1,230 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/ImageUploadConfig.vue b/apps/web-antd/src/views/common/form/config/ImageUploadConfig.vue new file mode 100644 index 0000000..156bd16 --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/ImageUploadConfig.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/LocationConfig.vue b/apps/web-antd/src/views/common/form/config/LocationConfig.vue new file mode 100644 index 0000000..08cc044 --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/LocationConfig.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/MoneyInputConfig.vue b/apps/web-antd/src/views/common/form/config/MoneyInputConfig.vue new file mode 100644 index 0000000..c91d66f --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/MoneyInputConfig.vue @@ -0,0 +1,14 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/NumberInputConfig.vue b/apps/web-antd/src/views/common/form/config/NumberInputConfig.vue new file mode 100644 index 0000000..26574a5 --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/NumberInputConfig.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/OuterFormConfig.vue b/apps/web-antd/src/views/common/form/config/OuterFormConfig.vue new file mode 100644 index 0000000..f3bdbb2 --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/OuterFormConfig.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/ProcessIndexConfig.vue b/apps/web-antd/src/views/common/form/config/ProcessIndexConfig.vue new file mode 100644 index 0000000..98b3f9e --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/ProcessIndexConfig.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/ProvincesConfig.vue b/apps/web-antd/src/views/common/form/config/ProvincesConfig.vue new file mode 100644 index 0000000..48578ca --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/ProvincesConfig.vue @@ -0,0 +1,36 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/ScoreConfig.vue b/apps/web-antd/src/views/common/form/config/ScoreConfig.vue new file mode 100644 index 0000000..7bd0a8b --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/ScoreConfig.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/SelectInputConfig.vue b/apps/web-antd/src/views/common/form/config/SelectInputConfig.vue new file mode 100644 index 0000000..4331fe3 --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/SelectInputConfig.vue @@ -0,0 +1,92 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/SelectPlusConfig.vue b/apps/web-antd/src/views/common/form/config/SelectPlusConfig.vue new file mode 100644 index 0000000..479dbdd --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/SelectPlusConfig.vue @@ -0,0 +1,175 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/SignPanelConfig.vue b/apps/web-antd/src/views/common/form/config/SignPanelConfig.vue new file mode 100644 index 0000000..aec7775 --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/SignPanelConfig.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/SpanLayoutConfig.vue b/apps/web-antd/src/views/common/form/config/SpanLayoutConfig.vue new file mode 100644 index 0000000..700bee9 --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/SpanLayoutConfig.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/TableListConfig.vue b/apps/web-antd/src/views/common/form/config/TableListConfig.vue new file mode 100644 index 0000000..5e934a4 --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/TableListConfig.vue @@ -0,0 +1,53 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/TextInputConfig.vue b/apps/web-antd/src/views/common/form/config/TextInputConfig.vue new file mode 100644 index 0000000..9e4448d --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/TextInputConfig.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/TextareaInputConfig.vue b/apps/web-antd/src/views/common/form/config/TextareaInputConfig.vue new file mode 100644 index 0000000..fca9116 --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/TextareaInputConfig.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/TimePickerConfig.vue b/apps/web-antd/src/views/common/form/config/TimePickerConfig.vue new file mode 100644 index 0000000..9386cee --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/TimePickerConfig.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/TimeRangePickerConfig.vue b/apps/web-antd/src/views/common/form/config/TimeRangePickerConfig.vue new file mode 100644 index 0000000..8c59ba1 --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/TimeRangePickerConfig.vue @@ -0,0 +1,39 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/UserPickerConfig.vue b/apps/web-antd/src/views/common/form/config/UserPickerConfig.vue new file mode 100644 index 0000000..e0a5724 --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/UserPickerConfig.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/VueContainerConfig.vue b/apps/web-antd/src/views/common/form/config/VueContainerConfig.vue new file mode 100644 index 0000000..502d8f2 --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/VueContainerConfig.vue @@ -0,0 +1,125 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/WebIframeConfig.vue b/apps/web-antd/src/views/common/form/config/WebIframeConfig.vue new file mode 100644 index 0000000..0444a1f --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/WebIframeConfig.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/subs/ActionConfig.vue b/apps/web-antd/src/views/common/form/config/subs/ActionConfig.vue new file mode 100644 index 0000000..cefc2aa --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/subs/ActionConfig.vue @@ -0,0 +1,114 @@ + + + + + diff --git a/apps/web-antd/src/views/common/form/config/subs/ConditionConfig.vue b/apps/web-antd/src/views/common/form/config/subs/ConditionConfig.vue new file mode 100644 index 0000000..d9fef52 --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/subs/ConditionConfig.vue @@ -0,0 +1,278 @@ + + + + diff --git a/apps/web-antd/src/views/common/form/config/subs/ConditionItem.vue b/apps/web-antd/src/views/common/form/config/subs/ConditionItem.vue new file mode 100644 index 0000000..e627e34 --- /dev/null +++ b/apps/web-antd/src/views/common/form/config/subs/ConditionItem.vue @@ -0,0 +1,124 @@ + + + + diff --git a/apps/web-antd/src/views/common/process/OrgItems.vue b/apps/web-antd/src/views/common/process/OrgItems.vue new file mode 100644 index 0000000..973fbff --- /dev/null +++ b/apps/web-antd/src/views/common/process/OrgItems.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/config/ApprovalNodeConfig.vue b/apps/web-antd/src/views/common/process/config/ApprovalNodeConfig.vue new file mode 100644 index 0000000..40c1daa --- /dev/null +++ b/apps/web-antd/src/views/common/process/config/ApprovalNodeConfig.vue @@ -0,0 +1,441 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/config/CcNodeConfig.vue b/apps/web-antd/src/views/common/process/config/CcNodeConfig.vue new file mode 100644 index 0000000..9796883 --- /dev/null +++ b/apps/web-antd/src/views/common/process/config/CcNodeConfig.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/config/ConcurrentNodeConfig.vue b/apps/web-antd/src/views/common/process/config/ConcurrentNodeConfig.vue new file mode 100644 index 0000000..f3d8e3a --- /dev/null +++ b/apps/web-antd/src/views/common/process/config/ConcurrentNodeConfig.vue @@ -0,0 +1,16 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/config/ConditionGroupItemConfig.vue b/apps/web-antd/src/views/common/process/config/ConditionGroupItemConfig.vue new file mode 100644 index 0000000..b1bf359 --- /dev/null +++ b/apps/web-antd/src/views/common/process/config/ConditionGroupItemConfig.vue @@ -0,0 +1,292 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/config/ConditionNodeConfig.vue b/apps/web-antd/src/views/common/process/config/ConditionNodeConfig.vue new file mode 100644 index 0000000..e9b1e9b --- /dev/null +++ b/apps/web-antd/src/views/common/process/config/ConditionNodeConfig.vue @@ -0,0 +1,168 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/config/DelayNodeConfig.vue b/apps/web-antd/src/views/common/process/config/DelayNodeConfig.vue new file mode 100644 index 0000000..9ee0d83 --- /dev/null +++ b/apps/web-antd/src/views/common/process/config/DelayNodeConfig.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/config/FormAuthorityConfig.vue b/apps/web-antd/src/views/common/process/config/FormAuthorityConfig.vue new file mode 100644 index 0000000..5e7a8f3 --- /dev/null +++ b/apps/web-antd/src/views/common/process/config/FormAuthorityConfig.vue @@ -0,0 +1,147 @@ + + + + + + diff --git a/apps/web-antd/src/views/common/process/config/InclusiveNodeConfig.vue b/apps/web-antd/src/views/common/process/config/InclusiveNodeConfig.vue new file mode 100644 index 0000000..0e4b008 --- /dev/null +++ b/apps/web-antd/src/views/common/process/config/InclusiveNodeConfig.vue @@ -0,0 +1,145 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/config/NodeConfig.vue b/apps/web-antd/src/views/common/process/config/NodeConfig.vue new file mode 100644 index 0000000..1d6dbc5 --- /dev/null +++ b/apps/web-antd/src/views/common/process/config/NodeConfig.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/config/RootNodeConfig.vue b/apps/web-antd/src/views/common/process/config/RootNodeConfig.vue new file mode 100644 index 0000000..ad62dff --- /dev/null +++ b/apps/web-antd/src/views/common/process/config/RootNodeConfig.vue @@ -0,0 +1,58 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/config/SubProcNodeConfig.vue b/apps/web-antd/src/views/common/process/config/SubProcNodeConfig.vue new file mode 100644 index 0000000..82c0c0f --- /dev/null +++ b/apps/web-antd/src/views/common/process/config/SubProcNodeConfig.vue @@ -0,0 +1,205 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/config/TaskListenerConfig.vue b/apps/web-antd/src/views/common/process/config/TaskListenerConfig.vue new file mode 100644 index 0000000..c8e216d --- /dev/null +++ b/apps/web-antd/src/views/common/process/config/TaskListenerConfig.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/config/TaskNodeConfig.vue b/apps/web-antd/src/views/common/process/config/TaskNodeConfig.vue new file mode 100644 index 0000000..6334b1b --- /dev/null +++ b/apps/web-antd/src/views/common/process/config/TaskNodeConfig.vue @@ -0,0 +1,401 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/config/TaskOperationConfig.vue b/apps/web-antd/src/views/common/process/config/TaskOperationConfig.vue new file mode 100644 index 0000000..b232b14 --- /dev/null +++ b/apps/web-antd/src/views/common/process/config/TaskOperationConfig.vue @@ -0,0 +1,98 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/config/TriggerNodeConfig.vue b/apps/web-antd/src/views/common/process/config/TriggerNodeConfig.vue new file mode 100644 index 0000000..1819888 --- /dev/null +++ b/apps/web-antd/src/views/common/process/config/TriggerNodeConfig.vue @@ -0,0 +1,218 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/nodes/ApprovalNode.vue b/apps/web-antd/src/views/common/process/nodes/ApprovalNode.vue new file mode 100644 index 0000000..20b23dd --- /dev/null +++ b/apps/web-antd/src/views/common/process/nodes/ApprovalNode.vue @@ -0,0 +1,255 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/nodes/CcNode.vue b/apps/web-antd/src/views/common/process/nodes/CcNode.vue new file mode 100644 index 0000000..55a099e --- /dev/null +++ b/apps/web-antd/src/views/common/process/nodes/CcNode.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/nodes/ConcurrentNode.vue b/apps/web-antd/src/views/common/process/nodes/ConcurrentNode.vue new file mode 100644 index 0000000..cb4573a --- /dev/null +++ b/apps/web-antd/src/views/common/process/nodes/ConcurrentNode.vue @@ -0,0 +1,237 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/nodes/ConditionNode.vue b/apps/web-antd/src/views/common/process/nodes/ConditionNode.vue new file mode 100644 index 0000000..09db39a --- /dev/null +++ b/apps/web-antd/src/views/common/process/nodes/ConditionNode.vue @@ -0,0 +1,418 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/nodes/DelayNode.vue b/apps/web-antd/src/views/common/process/nodes/DelayNode.vue new file mode 100644 index 0000000..3932d21 --- /dev/null +++ b/apps/web-antd/src/views/common/process/nodes/DelayNode.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/nodes/EmptyNode.vue b/apps/web-antd/src/views/common/process/nodes/EmptyNode.vue new file mode 100644 index 0000000..068fc39 --- /dev/null +++ b/apps/web-antd/src/views/common/process/nodes/EmptyNode.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/nodes/InclusiveNode.vue b/apps/web-antd/src/views/common/process/nodes/InclusiveNode.vue new file mode 100644 index 0000000..26dcf40 --- /dev/null +++ b/apps/web-antd/src/views/common/process/nodes/InclusiveNode.vue @@ -0,0 +1,401 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/nodes/JumpNode.vue b/apps/web-antd/src/views/common/process/nodes/JumpNode.vue new file mode 100644 index 0000000..72dafa4 --- /dev/null +++ b/apps/web-antd/src/views/common/process/nodes/JumpNode.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/nodes/Node.vue b/apps/web-antd/src/views/common/process/nodes/Node.vue new file mode 100644 index 0000000..0a4d081 --- /dev/null +++ b/apps/web-antd/src/views/common/process/nodes/Node.vue @@ -0,0 +1,258 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/nodes/RootNode.vue b/apps/web-antd/src/views/common/process/nodes/RootNode.vue new file mode 100644 index 0000000..5a2a747 --- /dev/null +++ b/apps/web-antd/src/views/common/process/nodes/RootNode.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/nodes/SubProcNode.vue b/apps/web-antd/src/views/common/process/nodes/SubProcNode.vue new file mode 100644 index 0000000..eb1a4fd --- /dev/null +++ b/apps/web-antd/src/views/common/process/nodes/SubProcNode.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/nodes/TaskNode.vue b/apps/web-antd/src/views/common/process/nodes/TaskNode.vue new file mode 100644 index 0000000..5df5dbd --- /dev/null +++ b/apps/web-antd/src/views/common/process/nodes/TaskNode.vue @@ -0,0 +1,237 @@ + + + + + diff --git a/apps/web-antd/src/views/common/process/nodes/TriggerNode.vue b/apps/web-antd/src/views/common/process/nodes/TriggerNode.vue new file mode 100644 index 0000000..af87b59 --- /dev/null +++ b/apps/web-antd/src/views/common/process/nodes/TriggerNode.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/apps/web-antd/src/views/dashboard/analytics/analytics-dynamic-task.vue b/apps/web-antd/src/views/dashboard/analytics/analytics-dynamic-task.vue new file mode 100644 index 0000000..bb785e2 --- /dev/null +++ b/apps/web-antd/src/views/dashboard/analytics/analytics-dynamic-task.vue @@ -0,0 +1,53 @@ + + diff --git a/apps/web-antd/src/views/dashboard/analytics/analytics-trends.vue b/apps/web-antd/src/views/dashboard/analytics/analytics-trends.vue new file mode 100644 index 0000000..c557680 --- /dev/null +++ b/apps/web-antd/src/views/dashboard/analytics/analytics-trends.vue @@ -0,0 +1,157 @@ + + + + diff --git a/apps/web-antd/src/views/dashboard/analytics/analytics-visits-data.vue b/apps/web-antd/src/views/dashboard/analytics/analytics-visits-data.vue new file mode 100644 index 0000000..8ed9d89 --- /dev/null +++ b/apps/web-antd/src/views/dashboard/analytics/analytics-visits-data.vue @@ -0,0 +1,76 @@ + + + diff --git a/apps/web-antd/src/views/dashboard/analytics/analytics-visits-sales.vue b/apps/web-antd/src/views/dashboard/analytics/analytics-visits-sales.vue new file mode 100644 index 0000000..ec934d4 --- /dev/null +++ b/apps/web-antd/src/views/dashboard/analytics/analytics-visits-sales.vue @@ -0,0 +1,92 @@ + + + + diff --git a/apps/web-antd/src/views/dashboard/analytics/analytics-visits-source.vue b/apps/web-antd/src/views/dashboard/analytics/analytics-visits-source.vue new file mode 100644 index 0000000..c899467 --- /dev/null +++ b/apps/web-antd/src/views/dashboard/analytics/analytics-visits-source.vue @@ -0,0 +1,83 @@ + + + + diff --git a/apps/web-antd/src/views/dashboard/analytics/analytics-visits.vue b/apps/web-antd/src/views/dashboard/analytics/analytics-visits.vue new file mode 100644 index 0000000..b5c337a --- /dev/null +++ b/apps/web-antd/src/views/dashboard/analytics/analytics-visits.vue @@ -0,0 +1,62 @@ + + + + diff --git a/apps/web-antd/src/views/dashboard/analytics/index copy.vue b/apps/web-antd/src/views/dashboard/analytics/index copy.vue new file mode 100644 index 0000000..57375bc --- /dev/null +++ b/apps/web-antd/src/views/dashboard/analytics/index copy.vue @@ -0,0 +1,280 @@ + + + + + diff --git a/apps/web-antd/src/views/dashboard/analytics/index.vue b/apps/web-antd/src/views/dashboard/analytics/index.vue new file mode 100644 index 0000000..6cc7600 --- /dev/null +++ b/apps/web-antd/src/views/dashboard/analytics/index.vue @@ -0,0 +1,595 @@ + + + + + + + + diff --git a/apps/web-antd/src/views/dashboard/workspace/index.vue b/apps/web-antd/src/views/dashboard/workspace/index.vue new file mode 100644 index 0000000..b95d613 --- /dev/null +++ b/apps/web-antd/src/views/dashboard/workspace/index.vue @@ -0,0 +1,266 @@ + + + diff --git a/apps/web-antd/src/views/im/group/data.ts b/apps/web-antd/src/views/im/group/data.ts new file mode 100644 index 0000000..2bcfc13 --- /dev/null +++ b/apps/web-antd/src/views/im/group/data.ts @@ -0,0 +1,186 @@ +/* + * @Author: Mm + * @Date: 2025-05-06 16:42:26 + * @LastEditors: Mm + * @LastEditTime: 2025-05-07 09:41:51 + * 不写bug的程序员不是一个好程序员! + */ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +import { getDictOptions } from '#/utils/dict'; +import { renderDict } from '#/utils/render'; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'name', + label: '群名', + }, + + { + component: 'Select', + componentProps: { + // 可选从DictEnum中获取 DictEnum.IM_BOOL 便于维护 + options: getDictOptions('im_bool'), + }, + fieldName: 'isBanned', + label: '是否封禁', + }, + + { + component: 'Select', + componentProps: { + // 可选从DictEnum中获取 DictEnum.IM_BOOL 便于维护 + options: getDictOptions('im_bool'), + }, + fieldName: 'dissolve', + label: '是否解散', + }, + { + component: 'RangePicker', + fieldName: 'createdTime', + label: '创建时间', + }, +]; + +// 需要使用i18n注意这里要改成getter形式 否则切换语言不会刷新 +// export const columns: () => VxeGridProps['columns'] = () => [ +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + + { + title: '群名', + field: 'name', + }, + + { + title: '群头像', + field: 'headImage', + slots:{default:'headImage'} + }, + + { + title: '群公告', + field: 'notice', + }, + { + title: '是否被封禁', + field: 'isBanned', + slots: { + default: ({ row }) => { + // 可选从DictEnum中获取 DictEnum.IM_BOOL 便于维护 + return row.isBanned?renderDict(row.isBanned, 'im_bool'):''; + }, + }, + }, + { + title: '被封禁原因', + field: 'reason', + }, + { + title: '是否已解散', + field: 'dissolve', + slots: { + default: ({ row }) => { + // 可选从DictEnum中获取 DictEnum.IM_BOOL 便于维护 + return row.dissolve?renderDict(row.dissolve, 'im_bool'):''; + }, + }, + }, + { + title: '创建时间', + field: 'createdTime', + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 180, + }, +]; + +export const modalSchema: FormSchemaGetter = () => [ + { + label: 'id', + fieldName: 'id', + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + }, + { + label: '群名字', + fieldName: 'name', + component: 'Input', + rules: 'required', + }, + { + label: '群主id', + fieldName: 'ownerId', + component: 'Input', + rules: 'required', + }, + { + label: '群头像', + fieldName: 'headImage', + component: 'ImageUpload', + componentProps: { + // accept: ['jpg'], // 不支持type/*的写法 支持拓展名(不带.) 文件头(image/png这种) + // maxNumber: 1, // 最大上传文件数 默认为1 为1会绑定为string而非string[]类型 + // resultField: 'url', // 上传成功后返回的字段名 默认url 可选['ossId', 'url', 'fileName'] + }, + rules: 'required', + }, + { + label: '群头像缩略图', + fieldName: 'headImageThumb', + component: 'Input', + rules: 'required', + }, + { + label: '群公告', + fieldName: 'notice', + component: 'Input', + rules: 'required', + }, + { + label: '是否被封禁', + fieldName: 'isBanned', + component: 'Select', + componentProps: { + // 可选从DictEnum中获取 DictEnum.IM_BOOL 便于维护 + options: getDictOptions('im_bool'), + }, + rules: 'selectRequired', + }, + { + label: '被封禁原因', + fieldName: 'reason', + component: 'Input', + rules: 'required', + }, + { + label: '是否已解散', + fieldName: 'dissolve', + component: 'Select', + componentProps: { + // 可选从DictEnum中获取 DictEnum.IM_BOOL 便于维护 + options: getDictOptions('im_bool'), + }, + rules: 'selectRequired', + }, + { + label: '创建时间', + fieldName: 'createdTime', + component: 'DatePicker', + componentProps: { + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'YYYY-MM-DD HH:mm:ss', + }, + rules: 'required', + }, +]; diff --git a/apps/web-antd/src/views/im/group/group-modal.vue b/apps/web-antd/src/views/im/group/group-modal.vue new file mode 100644 index 0000000..2e3fe8c --- /dev/null +++ b/apps/web-antd/src/views/im/group/group-modal.vue @@ -0,0 +1,92 @@ + + + + diff --git a/apps/web-antd/src/views/im/group/index.vue b/apps/web-antd/src/views/im/group/index.vue new file mode 100644 index 0000000..502f1d2 --- /dev/null +++ b/apps/web-antd/src/views/im/group/index.vue @@ -0,0 +1,193 @@ + + + + diff --git a/apps/web-antd/src/views/im/sensitiveWord/data.ts b/apps/web-antd/src/views/im/sensitiveWord/data.ts new file mode 100644 index 0000000..ddf3fc6 --- /dev/null +++ b/apps/web-antd/src/views/im/sensitiveWord/data.ts @@ -0,0 +1,84 @@ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Textarea', + fieldName: 'content', + label: '敏感词内容', + }, + { + component: 'Select', + componentProps: { + }, + fieldName: 'enabled', + label: '是否启用 0:未启用 1:启用', + }, + { + component: 'Input', + fieldName: 'creator', + label: '创建者', + }, +]; + +// 需要使用i18n注意这里要改成getter形式 否则切换语言不会刷新 +// export const columns: () => VxeGridProps['columns'] = () => [ +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + { + title: 'id', + field: 'id', + }, + { + title: '敏感词内容', + field: 'content', + }, + { + title: '是否启用 0:未启用 1:启用', + field: 'enabled', + }, + { + title: '创建者', + field: 'creator', + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 180, + }, +]; + +export const modalSchema: FormSchemaGetter = () => [ + { + label: 'id', + fieldName: 'id', + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + }, + { + label: '敏感词内容', + fieldName: 'content', + component: 'Textarea', + rules: 'required', + }, + { + label: '是否启用 0:未启用 1:启用', + fieldName: 'enabled', + component: 'Select', + componentProps: { + }, + rules: 'selectRequired', + }, + { + label: '创建者', + fieldName: 'creator', + component: 'Input', + rules: 'required', + }, +]; diff --git a/apps/web-antd/src/views/im/sensitiveWord/index.vue b/apps/web-antd/src/views/im/sensitiveWord/index.vue new file mode 100644 index 0000000..a90ee0a --- /dev/null +++ b/apps/web-antd/src/views/im/sensitiveWord/index.vue @@ -0,0 +1,186 @@ + + + diff --git a/apps/web-antd/src/views/im/sensitiveWord/sensitiveWord-modal.vue b/apps/web-antd/src/views/im/sensitiveWord/sensitiveWord-modal.vue new file mode 100644 index 0000000..ab0cad9 --- /dev/null +++ b/apps/web-antd/src/views/im/sensitiveWord/sensitiveWord-modal.vue @@ -0,0 +1,92 @@ + + + + diff --git a/apps/web-antd/src/views/im/user/data.ts b/apps/web-antd/src/views/im/user/data.ts new file mode 100644 index 0000000..12c29d8 --- /dev/null +++ b/apps/web-antd/src/views/im/user/data.ts @@ -0,0 +1,217 @@ +import { readonly } from 'vue'; +/* + * @Author: Mm + * @Date: 2025-05-06 16:20:51 + * @LastEditors: Mm + * @LastEditTime: 2025-05-07 10:06:43 + * 不写bug的程序员不是一个好程序员! + */ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +import { getDictOptions } from '#/utils/dict'; +import { renderDict } from '#/utils/render'; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'userName', + label: '用户账号', + }, + { + component: 'Input', + fieldName: 'nickName', + label: '用户昵称', + }, + + + { + component: 'Select', + componentProps: { + // 可选从DictEnum中获取 DictEnum.IM_BOOL 便于维护 + options: getDictOptions('im_bool'), + }, + fieldName: 'isBanned', + label: '是否封禁', + }, + { + component: 'Input', + fieldName: 'reason', + label: '被封禁原因', + }, + + { + component: 'RangePicker', + fieldName: 'createdTime', + label: '创建时间', + }, +]; + +// 需要使用i18n注意这里要改成getter形式 否则切换语言不会刷新 +// export const columns: () => VxeGridProps['columns'] = () => [ +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + + { + title: '名称', + field: 'userName', + }, + { + title: '昵称', + field: 'nickName', + }, + { + title: '头像', + field: 'headImage', + slots: { default: 'headImage' }, + }, + + { + title: '性别', + field: 'sex', + slots: { + default: ({ row }) => { + // 可选从DictEnum中获取 DictEnum.SYS_USER_SEX 便于维护 + return renderDict(row.sex, 'sys_user_sex') ; + }, + }, + }, + { + title: '是否被封禁', + field: 'isBanned', + slots: { + default: ({ row }) => { + // 可选从DictEnum中获取 DictEnum.IM_BOOL 便于维护 + return row.isBanned ? renderDict(row.isBanned, 'im_bool') : ''; + }, + }, + }, + // { + // title: '被封禁原因', + // field: 'reason', + // }, + + { + title: '最后登录时间', + field: 'lastLoginTime', + }, + { + title: '创建时间', + field: 'createdTime', + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 180, + }, +]; + +export const modalSchema: FormSchemaGetter = () => [ + { + label: 'id', + fieldName: 'id', + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + }, + { + label: '用户头像', + fieldName: 'headImage', + component: 'ImageUpload', + formItemClass: 'col-span-2', + }, + { + label: '用户账号', + fieldName: 'userName', + component: 'Input', + componentProps: { + readonly: true, + }, + }, + { + label: '用户昵称', + fieldName: 'nickName', + component: 'Input', + componentProps: { + readonly: true, + }, + }, + + { + label: '用户性别', + fieldName: 'sex', + component: 'Input', + componentProps: { + readonly: true, + }, + }, + { + label: '是否封禁', + fieldName: 'isBanned', + component: 'Input', + componentProps: { + readonly: true, + }, + }, + { + label: '封禁原因', + fieldName: 'reason', + component: 'Input', + componentProps: { + readonly: true, + }, + }, + + { + label: '个性签名', + fieldName: 'signature', + component: 'Input', + componentProps: { + readonly: true, + }, + }, + + { + label: '最后登录时间', + fieldName: 'lastLoginTime', + component: 'Input', + componentProps: { + readonly: true, + }, + }, + { + label: '创建时间', + fieldName: 'createdTime', + component: 'Input', + componentProps: { + readonly: true, + }, + }, +]; + +export const modalBanSchema: FormSchemaGetter = () => [ + { + label: 'id', + fieldName: 'id', + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + }, + + { + label: '封禁原因', + fieldName: 'reason', + component: 'Textarea', + componentProps:{ + placeholder:'请输入封禁原因' + } + + }, + +]; diff --git a/apps/web-antd/src/views/im/user/index.vue b/apps/web-antd/src/views/im/user/index.vue new file mode 100644 index 0000000..65c057b --- /dev/null +++ b/apps/web-antd/src/views/im/user/index.vue @@ -0,0 +1,229 @@ + + + + diff --git a/apps/web-antd/src/views/im/user/user-ban-modal.vue b/apps/web-antd/src/views/im/user/user-ban-modal.vue new file mode 100644 index 0000000..58f6717 --- /dev/null +++ b/apps/web-antd/src/views/im/user/user-ban-modal.vue @@ -0,0 +1,107 @@ + + + + diff --git a/apps/web-antd/src/views/im/user/user-modal.vue b/apps/web-antd/src/views/im/user/user-modal.vue new file mode 100644 index 0000000..14daca2 --- /dev/null +++ b/apps/web-antd/src/views/im/user/user-modal.vue @@ -0,0 +1,105 @@ + + + + + diff --git a/apps/web-antd/src/views/monitor/admin/index.vue b/apps/web-antd/src/views/monitor/admin/index.vue new file mode 100644 index 0000000..da1b7a2 --- /dev/null +++ b/apps/web-antd/src/views/monitor/admin/index.vue @@ -0,0 +1,10 @@ + + diff --git a/apps/web-antd/src/views/monitor/cache/components/command-chart.vue b/apps/web-antd/src/views/monitor/cache/components/command-chart.vue new file mode 100644 index 0000000..df17a1c --- /dev/null +++ b/apps/web-antd/src/views/monitor/cache/components/command-chart.vue @@ -0,0 +1,74 @@ + + + diff --git a/apps/web-antd/src/views/monitor/cache/components/index.ts b/apps/web-antd/src/views/monitor/cache/components/index.ts new file mode 100644 index 0000000..1978212 --- /dev/null +++ b/apps/web-antd/src/views/monitor/cache/components/index.ts @@ -0,0 +1,3 @@ +export { default as CommandChart } from './command-chart.vue'; +export { default as MemoryChart } from './memory-chart.vue'; +export { default as RedisDescription } from './redis-description.vue'; diff --git a/apps/web-antd/src/views/monitor/cache/components/memory-chart.vue b/apps/web-antd/src/views/monitor/cache/components/memory-chart.vue new file mode 100644 index 0000000..4f18a6c --- /dev/null +++ b/apps/web-antd/src/views/monitor/cache/components/memory-chart.vue @@ -0,0 +1,98 @@ + + + diff --git a/apps/web-antd/src/views/monitor/cache/components/redis-description.vue b/apps/web-antd/src/views/monitor/cache/components/redis-description.vue new file mode 100644 index 0000000..44a3c63 --- /dev/null +++ b/apps/web-antd/src/views/monitor/cache/components/redis-description.vue @@ -0,0 +1,103 @@ + + + diff --git a/apps/web-antd/src/views/monitor/cache/index.vue b/apps/web-antd/src/views/monitor/cache/index.vue new file mode 100644 index 0000000..9ac4586 --- /dev/null +++ b/apps/web-antd/src/views/monitor/cache/index.vue @@ -0,0 +1,107 @@ + + + diff --git a/apps/web-antd/src/views/monitor/logininfor/data.tsx b/apps/web-antd/src/views/monitor/logininfor/data.tsx new file mode 100644 index 0000000..aee1963 --- /dev/null +++ b/apps/web-antd/src/views/monitor/logininfor/data.tsx @@ -0,0 +1,168 @@ +import type { VNode } from 'vue'; + +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; +import type { DescItem } from '#/components/description'; + +import { DictEnum } from '@vben/constants'; + +import { getDictOptions } from '#/utils/dict'; +import { renderBrowserIcon, renderDict, renderOsIcon } from '#/utils/render'; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'ipaddr', + label: 'IP地址', + }, + { + component: 'Input', + fieldName: 'userName', + label: '用户账号', + }, + { + component: 'Select', + componentProps: { + options: getDictOptions(DictEnum.SYS_COMMON_STATUS), + }, + fieldName: 'status', + label: '登录状态', + }, + { + component: 'RangePicker', + fieldName: 'dateTime', + label: '登录日期', + }, +]; + +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + { + title: '用户账号', + field: 'userName', + }, + { + title: '登录平台', + field: 'clientKey', + }, + { + title: 'IP地址', + field: 'ipaddr', + }, + { + title: 'IP地点', + field: 'loginLocation', + width: 200, + }, + { + title: '浏览器', + field: 'browser', + slots: { + default: ({ row }) => { + return renderBrowserIcon(row.browser, true) as VNode; + }, + }, + }, + { + title: '系统', + field: 'os', + slots: { + default: ({ row }) => { + /** + * Windows 10 or Windows Server 2016 太长了 分割一下 详情依旧能看到详细的 + */ + let value = row.os; + if (value) { + const split = value.split(' or '); + if (split.length === 2) { + value = split[0]; + } + } + return renderOsIcon(value, true) as VNode; + }, + }, + }, + { + title: '登录结果', + field: 'status', + slots: { + default: ({ row }) => { + return renderDict(row.status, DictEnum.SYS_COMMON_STATUS); + }, + }, + }, + { + title: '信息', + field: 'msg', + }, + { + title: '日期', + field: 'loginTime', + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 150, + }, +]; + +export const modalSchema: () => DescItem[] = () => [ + { + field: 'status', + label: '登录状态', + labelMinWidth: 80, + render(value) { + return renderDict(value, DictEnum.SYS_COMMON_STATUS); + }, + }, + { + field: 'clientKey', + label: '登录平台', + render(value) { + if (value) { + return value.toUpperCase(); + } + return ''; + }, + }, + { + field: 'ipaddr', + label: '账号信息', + render(_, data) { + const { ipaddr, loginLocation, userName } = data; + return `账号: ${userName} / ${ipaddr} / ${loginLocation}`; + }, + }, + { + field: 'loginTime', + label: '登录时间', + }, + { + field: 'msg', + label: '登录信息', + render(_, data: any) { + const { msg, status } = data; + return ( + + {msg} + + ); + }, + }, + { + field: 'os', + label: '登录设备', + render(value) { + return renderOsIcon(value); + }, + }, + { + field: 'browser', + label: '浏览器', + render(value) { + return renderBrowserIcon(value); + }, + }, +]; diff --git a/apps/web-antd/src/views/monitor/logininfor/index.vue b/apps/web-antd/src/views/monitor/logininfor/index.vue new file mode 100644 index 0000000..aa8a15a --- /dev/null +++ b/apps/web-antd/src/views/monitor/logininfor/index.vue @@ -0,0 +1,210 @@ + + + diff --git a/apps/web-antd/src/views/monitor/logininfor/login-info-modal.vue b/apps/web-antd/src/views/monitor/logininfor/login-info-modal.vue new file mode 100644 index 0000000..9f9eadc --- /dev/null +++ b/apps/web-antd/src/views/monitor/logininfor/login-info-modal.vue @@ -0,0 +1,33 @@ + + + diff --git a/apps/web-antd/src/views/monitor/online/data.ts b/apps/web-antd/src/views/monitor/online/data.ts new file mode 100644 index 0000000..45e1f04 --- /dev/null +++ b/apps/web-antd/src/views/monitor/online/data.ts @@ -0,0 +1,91 @@ +/* + * @Author: hcc han_chen301@163.com + * @Date: 2025-05-23 09:27:19 + * @LastEditors: hcc + * @LastEditTime: 2025-05-23 11:50:58 + * @remarker: + */ +import type { VNode } from 'vue'; + +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +import dayjs from 'dayjs'; + +import { renderBrowserIcon, renderOsIcon } from '#/utils/render'; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'ipaddr', + label: 'IP地址', + }, + { + component: 'Input', + fieldName: 'userName', + label: '用户账号', + }, +]; + +export const columns: VxeGridProps['columns'] = [ + { + title: '登录平台', + field: 'deviceType', + }, + { + title: '登录账号', + field: 'userName', + }, + { + title: '部门名称', + field: 'deptName', + }, + { + title: 'IP地址', + field: 'ipaddr', + }, + { + title: '登录地址', + field: 'loginLocation', + }, + { + title: '浏览器', + field: 'browser', + slots: { + default: ({ row }) => { + return renderBrowserIcon(row.browser, true) as VNode; + }, + }, + }, + { + title: '系统', + field: 'os', + slots: { + default: ({ row }) => { + // Windows 10 or Windows Server 2016 太长了 分割一下 详情依旧能看到详细的 + let value = row.os; + if (value) { + const split = value.split(' or '); + if (split.length === 2) { + value = split[0]; + } + } + return renderOsIcon(value, true) as VNode; + }, + }, + }, + { + title: '登录时间', + field: 'loginTime', + formatter: ({ cellValue }) => { + return dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss'); + }, + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 120, + }, +]; diff --git a/apps/web-antd/src/views/monitor/online/index.vue b/apps/web-antd/src/views/monitor/online/index.vue new file mode 100644 index 0000000..b557c8f --- /dev/null +++ b/apps/web-antd/src/views/monitor/online/index.vue @@ -0,0 +1,91 @@ + + + diff --git a/apps/web-antd/src/views/monitor/operlog/data.tsx b/apps/web-antd/src/views/monitor/operlog/data.tsx new file mode 100644 index 0000000..d99cbe8 --- /dev/null +++ b/apps/web-antd/src/views/monitor/operlog/data.tsx @@ -0,0 +1,199 @@ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; +import type { DescItem } from '#/components/description'; + +import { DictEnum } from '@vben/constants'; + +import { Tag } from 'ant-design-vue'; + +import { getDictOptions } from '#/utils/dict'; +import { + renderDict, + renderHttpMethodTag, + renderJsonPreview, +} from '#/utils/render'; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'title', + label: '系统模块', + }, + { + component: 'Input', + fieldName: 'operName', + label: '操作人员', + }, + { + component: 'Select', + componentProps: { + options: getDictOptions(DictEnum.SYS_OPER_TYPE), + }, + fieldName: 'businessType', + label: '操作类型', + }, + { + component: 'Input', + fieldName: 'operIp', + label: '操作IP', + }, + { + component: 'Select', + componentProps: { + options: getDictOptions(DictEnum.SYS_COMMON_STATUS), + }, + fieldName: 'status', + label: '状态', + }, + { + component: 'RangePicker', + fieldName: 'createTime', + label: '操作时间', + componentProps: { + valueFormat: 'YYYY-MM-DD HH:mm:ss', + }, + }, +]; + +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + { field: 'title', title: '系统模块' }, + { + title: '操作类型', + field: 'businessType', + slots: { + default: ({ row }) => { + return renderDict(row.businessType, DictEnum.SYS_OPER_TYPE); + }, + }, + }, + { field: 'operName', title: '操作人员' }, + { field: 'operIp', title: 'IP地址' }, + { field: 'operLocation', title: 'IP信息' }, + { + field: 'status', + title: '操作状态', + slots: { + default: ({ row }) => { + return renderDict(row.status, DictEnum.SYS_COMMON_STATUS); + }, + }, + }, + { field: 'operTime', title: '操作日期', sortable: true }, + { + field: 'costTime', + title: '操作耗时', + sortable: true, + formatter({ cellValue }) { + return `${cellValue} ms`; + }, + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 120, + }, +]; + +export const descSchema: DescItem[] = [ + { + field: 'operId', + label: '日志编号', + }, + { + field: 'status', + label: '操作结果', + render(value) { + return renderDict(value, DictEnum.SYS_COMMON_STATUS); + }, + }, + { + field: 'title', + label: '操作模块', + labelMinWidth: 80, + render(value, { businessType }) { + const operType = renderDict(businessType, DictEnum.SYS_OPER_TYPE); + return ( +
    + {value} + {operType} +
    + ); + }, + }, + { + field: 'operIp', + label: '操作信息', + render(_, data) { + return `账号: ${data.operName} / ${data.deptName} / ${data.operIp} / ${data.operLocation}`; + }, + }, + { + field: 'operUrl', + label: '请求信息', + render(_, data) { + const { operUrl, requestMethod } = data; + const methodTag = renderHttpMethodTag(requestMethod); + return ( + + {methodTag} {operUrl} + + ); + }, + }, + { + field: 'errorMsg', + label: '异常信息', + render(value) { + return {value}; + }, + show: (data) => { + return data && data.errorMsg !== ''; + }, + }, + { + field: 'method', + label: '方法', + }, + /** + * 默认word-break: break-word;会导致json预览样式异常 + */ + { + field: 'operParam', + label: '请求参数', + render(value) { + return ( +
    + {renderJsonPreview(value)} +
    + ); + }, + }, + { + field: 'jsonResult', + label: '响应参数', + render(value) { + return ( +
    + {renderJsonPreview(value)} +
    + ); + }, + show(data) { + return data && data.jsonResult; + }, + }, + { + field: 'costTime', + label: '耗时', + render(value) { + return `${value} ms`; + }, + }, + { + field: 'operTime', + label: '操作时间', + }, +]; diff --git a/apps/web-antd/src/views/monitor/operlog/index.vue b/apps/web-antd/src/views/monitor/operlog/index.vue new file mode 100644 index 0000000..2948a2b --- /dev/null +++ b/apps/web-antd/src/views/monitor/operlog/index.vue @@ -0,0 +1,184 @@ + + + diff --git a/apps/web-antd/src/views/monitor/operlog/operation-preview-drawer.vue b/apps/web-antd/src/views/monitor/operlog/operation-preview-drawer.vue new file mode 100644 index 0000000..e924cca --- /dev/null +++ b/apps/web-antd/src/views/monitor/operlog/operation-preview-drawer.vue @@ -0,0 +1,32 @@ + + + diff --git a/apps/web-antd/src/views/monitor/operlog/operation-preview-modal.vue b/apps/web-antd/src/views/monitor/operlog/operation-preview-modal.vue new file mode 100644 index 0000000..38b8e66 --- /dev/null +++ b/apps/web-antd/src/views/monitor/operlog/operation-preview-modal.vue @@ -0,0 +1,32 @@ + + + diff --git a/apps/web-antd/src/views/monitor/snailjob/index.vue b/apps/web-antd/src/views/monitor/snailjob/index.vue new file mode 100644 index 0000000..6b40353 --- /dev/null +++ b/apps/web-antd/src/views/monitor/snailjob/index.vue @@ -0,0 +1,3 @@ + diff --git a/apps/web-antd/src/views/process/ProcessNodeRender.vue b/apps/web-antd/src/views/process/ProcessNodeRender.vue new file mode 100644 index 0000000..040717b --- /dev/null +++ b/apps/web-antd/src/views/process/ProcessNodeRender.vue @@ -0,0 +1,91 @@ + + + + + diff --git a/apps/web-antd/src/views/process/ProcessRender.vue b/apps/web-antd/src/views/process/ProcessRender.vue new file mode 100644 index 0000000..d0fac4e --- /dev/null +++ b/apps/web-antd/src/views/process/ProcessRender.vue @@ -0,0 +1,798 @@ + + + + diff --git a/apps/web-antd/src/views/system/client/client-drawer.vue b/apps/web-antd/src/views/system/client/client-drawer.vue new file mode 100644 index 0000000..bcbed4e --- /dev/null +++ b/apps/web-antd/src/views/system/client/client-drawer.vue @@ -0,0 +1,133 @@ + + + + + diff --git a/apps/web-antd/src/views/system/client/client-modal.vue b/apps/web-antd/src/views/system/client/client-modal.vue new file mode 100644 index 0000000..059f95d --- /dev/null +++ b/apps/web-antd/src/views/system/client/client-modal.vue @@ -0,0 +1,133 @@ + + + + + diff --git a/apps/web-antd/src/views/system/client/data.tsx b/apps/web-antd/src/views/system/client/data.tsx new file mode 100644 index 0000000..82e48b4 --- /dev/null +++ b/apps/web-antd/src/views/system/client/data.tsx @@ -0,0 +1,290 @@ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +import { DictEnum } from '@vben/constants'; +import { getPopupContainer } from '@vben/utils'; + +import { getDictOptions } from '#/utils/dict'; +import { renderDict, renderDictTags } from '#/utils/render'; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'clientKey', + label: '客户端key', + }, + { + component: 'Input', + fieldName: 'clientSecret', + label: '客户端密钥', + }, + { + component: 'Select', + componentProps: { + options: getDictOptions(DictEnum.SYS_NORMAL_DISABLE), + }, + fieldName: 'status', + label: '状态', + }, +]; + +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + { + title: '客户端ID', + field: 'clientId', + showOverflow: true, + }, + { + title: '客户端key', + field: 'clientKey', + }, + { + title: '客户端密钥', + field: 'clientSecret', + }, + { + title: '授权类型', + field: 'grantTypeList', + slots: { + default: ({ row }) => { + if (!row.grantTypeList) { + return '无'; + } + return renderDictTags( + row.grantTypeList, + getDictOptions(DictEnum.SYS_GRANT_TYPE), + true, + 4, + ); + }, + }, + }, + { + title: '设备类型', + field: 'deviceType', + slots: { + default: ({ row }) => { + return renderDict(row.deviceType, DictEnum.SYS_DEVICE_TYPE); + }, + }, + }, + { + title: 'token活跃时间', + field: 'activeTimeout', + formatter({ row }) { + return `${row.activeTimeout}秒`; + }, + }, + { + title: 'token超时时间', + field: 'timeout', + formatter({ row }) { + return `${row.timeout}秒`; + }, + }, + { + title: '状态', + field: 'status', + slots: { + default: 'status', + }, + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 180, + }, +]; + +export const drawerSchema: FormSchemaGetter = () => [ + { + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'id', + label: 'id', + }, + { + component: 'Input', + componentProps: { + disabled: true, + }, + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'clientId', + label: '客户端ID', + }, + { + component: 'Input', + fieldName: 'clientKey', + label: '客户端key', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'clientSecret', + label: '客户端密钥', + rules: 'required', + }, + { + component: 'Select', + componentProps: { + getPopupContainer, + mode: 'multiple', + optionFilterProp: 'label', + options: getDictOptions(DictEnum.SYS_GRANT_TYPE), + }, + fieldName: 'grantTypeList', + label: '授权类型', + rules: 'selectRequired', + }, + { + component: 'Select', + componentProps: { + allowClear: false, + getPopupContainer, + options: getDictOptions(DictEnum.SYS_DEVICE_TYPE), + }, + fieldName: 'deviceType', + label: '设备类型', + rules: 'selectRequired', + }, + { + component: 'InputNumber', + componentProps: { + addonAfter: '秒', + placeholder: '请输入', + }, + defaultValue: 1800, + fieldName: 'activeTimeout', + formItemClass: 'col-span-2 lg:col-span-1', + help: '指定时间无操作则过期(单位:秒), 默认30分钟(1800秒)', + label: 'Token活跃超时时间', + rules: 'required', + }, + { + component: 'InputNumber', + componentProps: { + addonAfter: '秒', + }, + defaultValue: 604_800, + fieldName: 'timeout', + formItemClass: 'col-span-2 lg:col-span-1 ', + help: '指定时间必定过期(单位:秒),默认七天(604800秒)', + label: 'Token固定超时时间', + rules: 'required', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: getDictOptions(DictEnum.SYS_NORMAL_DISABLE), + optionType: 'button', + }, + defaultValue: '0', + fieldName: 'status', + label: '状态', + }, +]; + +export const modalSchema: FormSchemaGetter = () => [ + { + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'id', + label: 'id', + }, + { + component: 'Input', + componentProps: { + disabled: true, + }, + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'clientId', + label: '客户端ID', + }, + { + component: 'Input', + fieldName: 'clientKey', + label: '客户端key', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'clientSecret', + label: '客户端密钥', + rules: 'required', + }, + { + component: 'Select', + componentProps: { + getPopupContainer, + mode: 'multiple', + optionFilterProp: 'label', + options: getDictOptions(DictEnum.SYS_GRANT_TYPE), + }, + fieldName: 'grantTypeList', + label: '授权类型', + rules: 'selectRequired', + }, + { + component: 'Select', + componentProps: { + allowClear: false, + getPopupContainer, + options: getDictOptions(DictEnum.SYS_DEVICE_TYPE), + }, + fieldName: 'deviceType', + label: '设备类型', + rules: 'selectRequired', + }, + { + component: 'InputNumber', + componentProps: { + addonAfter: '秒', + placeholder: '请输入', + }, + defaultValue: 1800, + fieldName: 'activeTimeout', + formItemClass: 'col-span-2 lg:col-span-1', + help: '指定时间无操作则过期(单位:秒), 默认30分钟(1800秒)', + label: 'Token活跃超时时间', + rules: 'required', + }, + { + component: 'InputNumber', + componentProps: { + addonAfter: '秒', + }, + defaultValue: 604_800, + fieldName: 'timeout', + formItemClass: 'col-span-2 lg:col-span-1 ', + help: '指定时间必定过期(单位:秒),默认七天(604800秒)', + label: 'Token固定超时时间', + rules: 'required', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: getDictOptions(DictEnum.SYS_NORMAL_DISABLE), + optionType: 'button', + }, + defaultValue: '0', + fieldName: 'status', + label: '状态', + }, +]; diff --git a/apps/web-antd/src/views/system/client/index.vue b/apps/web-antd/src/views/system/client/index.vue new file mode 100644 index 0000000..b13426c --- /dev/null +++ b/apps/web-antd/src/views/system/client/index.vue @@ -0,0 +1,182 @@ + + + diff --git a/apps/web-antd/src/views/system/client/secret-input.vue b/apps/web-antd/src/views/system/client/secret-input.vue new file mode 100644 index 0000000..cc278cf --- /dev/null +++ b/apps/web-antd/src/views/system/client/secret-input.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/apps/web-antd/src/views/system/config/config-modal.vue b/apps/web-antd/src/views/system/config/config-modal.vue new file mode 100644 index 0000000..9569f0c --- /dev/null +++ b/apps/web-antd/src/views/system/config/config-modal.vue @@ -0,0 +1,78 @@ + + + diff --git a/apps/web-antd/src/views/system/config/data.ts b/apps/web-antd/src/views/system/config/data.ts new file mode 100644 index 0000000..ba03a5f --- /dev/null +++ b/apps/web-antd/src/views/system/config/data.ts @@ -0,0 +1,128 @@ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +import { DictEnum } from '@vben/constants'; +import { getPopupContainer } from '@vben/utils'; + +import { getDictOptions } from '#/utils/dict'; +import { renderDict } from '#/utils/render'; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'configName', + label: '参数名称', + }, + { + component: 'Input', + fieldName: 'configKey', + label: '参数键名', + }, + { + component: 'Select', + componentProps: { + getPopupContainer, + options: getDictOptions(DictEnum.SYS_YES_NO), + }, + fieldName: 'configType', + label: '系统内置', + }, + { + component: 'RangePicker', + fieldName: 'createTime', + label: '创建时间', + }, +]; + +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + { + title: '参数名称', + field: 'configName', + }, + { + title: '参数KEY', + field: 'configKey', + }, + { + title: '参数Value', + field: 'configValue', + }, + { + title: '系统内置', + field: 'configType', + width: 120, + slots: { + default: ({ row }) => { + return renderDict(row.configType, DictEnum.SYS_YES_NO); + }, + }, + }, + { + title: '备注', + field: 'remark', + }, + { + title: '创建时间', + field: 'createTime', + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 180, + }, +]; + +export const modalSchema: FormSchemaGetter = () => [ + { + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'configId', + label: '参数主键', + }, + { + component: 'Input', + fieldName: 'configName', + label: '参数名称', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'configKey', + label: '参数键名', + rules: 'required', + }, + { + component: 'Textarea', + formItemClass: 'items-baseline', + fieldName: 'configValue', + label: '参数键值', + componentProps: { + autoSize: true, + }, + rules: 'required', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: getDictOptions(DictEnum.SYS_YES_NO), + optionType: 'button', + }, + defaultValue: 'N', + fieldName: 'configType', + label: '是否内置', + rules: 'required', + }, + { + component: 'Textarea', + fieldName: 'remark', + formItemClass: 'items-baseline', + label: '备注', + }, +]; diff --git a/apps/web-antd/src/views/system/config/index.vue b/apps/web-antd/src/views/system/config/index.vue new file mode 100644 index 0000000..ceb1176 --- /dev/null +++ b/apps/web-antd/src/views/system/config/index.vue @@ -0,0 +1,177 @@ + + + diff --git a/apps/web-antd/src/views/system/dept/data.ts b/apps/web-antd/src/views/system/dept/data.ts new file mode 100644 index 0000000..86ed102 --- /dev/null +++ b/apps/web-antd/src/views/system/dept/data.ts @@ -0,0 +1,155 @@ +/* + * @Author: Mm + * @Date: 2025-03-10 10:03:34 + * @LastEditors: Mm + * @LastEditTime: 2025-03-10 14:26:37 + * 不写bug的程序员不是一个好程序员! + */ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +import { DictEnum } from '@vben/constants'; +import { getPopupContainer } from '@vben/utils'; + +import { z } from '#/adapter/form'; +import { getDictOptions } from '#/utils/dict'; +import { renderDict } from '#/utils/render'; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'deptName', + label: '部门名称', + }, + { + component: 'Select', + componentProps: { + getPopupContainer, + options: getDictOptions(DictEnum.SYS_NORMAL_DISABLE), + }, + fieldName: 'status', + label: '部门状态', + }, +]; + +export const columns: VxeGridProps['columns'] = [ + { + field: 'deptName', + title: '部门名称', + treeNode: true, + width: 200, + }, + // { + // field: 'deptCategory', + // title: '类别编码', + // }, + { + field: 'orderNum', + title: '排序', + width: 180, + }, + { + field: 'status', + width: 180, + title: '状态', + slots: { + default: ({ row }) => { + return renderDict(row.status, DictEnum.SYS_NORMAL_DISABLE); + }, + }, + }, + { + field: 'createTime', + title: '创建时间', + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 200, + }, +]; + +export const modalSchema: FormSchemaGetter = () => [ + { + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'deptId', + }, + { + component: 'TreeSelect', + componentProps: { + getPopupContainer, + }, + dependencies: { + show: (model) => model.parentId !== 0, + triggerFields: ['parentId'], + }, + fieldName: 'parentId', + label: '上级部门', + rules: 'selectRequired', + }, + { + component: 'Input', + fieldName: 'deptName', + label: '部门名称', + rules: 'required', + }, + { + component: 'InputNumber', + fieldName: 'orderNum', + label: '显示排序', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'deptCategory', + label: '类别编码', + }, + { + component: 'Select', + componentProps: { + // 选中了就只能修改 不能重置为无负责人 + allowClear: false, + // getPopupContainer, + showSearch: true, + }, + fieldName: 'leader', + label: '负责人', + }, + { + component: 'Input', + fieldName: 'phone', + label: '联系电话', + rules: z + .string() + .regex(/^1[3,4578]\d{9}$/, { message: '请输入正确的手机号' }) + .optional() + .or(z.literal('')), + }, + { + component: 'Input', + fieldName: 'email', + label: '邮箱', + rules: z + .string() + .email({ message: '请输入正确的邮箱' }) + .optional() + .or(z.literal('')), + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: getDictOptions(DictEnum.SYS_NORMAL_DISABLE), + optionType: 'button', + }, + defaultValue: '0', + fieldName: 'status', + label: '状态', + }, +]; diff --git a/apps/web-antd/src/views/system/dept/dept-modal.vue b/apps/web-antd/src/views/system/dept/dept-modal.vue new file mode 100644 index 0000000..b82cc1f --- /dev/null +++ b/apps/web-antd/src/views/system/dept/dept-modal.vue @@ -0,0 +1,180 @@ + + + diff --git a/apps/web-antd/src/views/system/dept/index.vue b/apps/web-antd/src/views/system/dept/index.vue new file mode 100644 index 0000000..6aa53e5 --- /dev/null +++ b/apps/web-antd/src/views/system/dept/index.vue @@ -0,0 +1,194 @@ + + + + diff --git a/apps/web-antd/src/views/system/dict/data/data.ts b/apps/web-antd/src/views/system/dict/data/data.ts new file mode 100644 index 0000000..56abe50 --- /dev/null +++ b/apps/web-antd/src/views/system/dict/data/data.ts @@ -0,0 +1,166 @@ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; +import type { DictData } from '#/api/system/dict/dict-data-model'; + +import { renderDictTag } from '#/utils/render'; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'dictLabel', + label: '字典标签', + }, +]; + +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + { + title: '字典标签', + field: 'cssClass', + slots: { + default: ({ row }) => { + const { dictValue } = row as DictData; + return renderDictTag(dictValue, [row]); + }, + }, + }, + { + title: '字典键值', + field: 'dictValue', + }, + { + title: '字典排序', + field: 'dictSort', + }, + { + title: '备注', + field: 'remark', + }, + { + title: '创建时间', + field: 'createTime', + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 180, + }, +]; + +export const drawerSchema: FormSchemaGetter = () => [ + { + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'dictCode', + }, + { + component: 'Input', + componentProps: { + disabled: true, + }, + fieldName: 'dictType', + label: '字典类型', + }, + { + component: 'Input', + fieldName: 'listClass', + label: '标签样式', + }, + { + component: 'Input', + fieldName: 'dictLabel', + label: '数据标签', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'dictValue', + label: '数据键值', + rules: 'required', + }, + { + component: 'Textarea', + componentProps: { + placeholder: '可使用tailwind类名 如bg-blue w-full h-full等', + }, + fieldName: 'cssClass', + formItemClass: 'items-baseline', + help: '标签的css样式, 可添加已经编译的css类名', + label: 'css类名', + }, + { + component: 'InputNumber', + fieldName: 'dictSort', + label: '显示排序', + rules: 'required', + }, + { + component: 'Textarea', + fieldName: 'remark', + formItemClass: 'items-baseline', + label: '备注', + }, +]; + +export const modalSchema: FormSchemaGetter = () => [ + { + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'dictCode', + }, + { + component: 'Input', + componentProps: { + disabled: true, + }, + fieldName: 'dictType', + label: '字典类型', + }, + { + component: 'Input', + fieldName: 'listClass', + label: '标签样式', + }, + { + component: 'Input', + fieldName: 'dictLabel', + label: '数据标签', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'dictValue', + label: '数据键值', + rules: 'required', + }, + { + component: 'Textarea', + componentProps: { + placeholder: '可使用tailwind类名 如bg-blue w-full h-full等', + }, + fieldName: 'cssClass', + formItemClass: 'items-baseline', + help: '标签的css样式, 可添加已经编译的css类名', + label: 'css类名', + }, + { + component: 'InputNumber', + fieldName: 'dictSort', + label: '显示排序', + rules: 'required', + }, + { + component: 'Textarea', + fieldName: 'remark', + formItemClass: 'items-baseline', + label: '备注', + }, +]; diff --git a/apps/web-antd/src/views/system/dict/data/dict-data-drawer.vue b/apps/web-antd/src/views/system/dict/data/dict-data-drawer.vue new file mode 100644 index 0000000..96ab3b0 --- /dev/null +++ b/apps/web-antd/src/views/system/dict/data/dict-data-drawer.vue @@ -0,0 +1,131 @@ + + + diff --git a/apps/web-antd/src/views/system/dict/data/dict-data-modal.vue b/apps/web-antd/src/views/system/dict/data/dict-data-modal.vue new file mode 100644 index 0000000..fcbde93 --- /dev/null +++ b/apps/web-antd/src/views/system/dict/data/dict-data-modal.vue @@ -0,0 +1,131 @@ + + + diff --git a/apps/web-antd/src/views/system/dict/data/index.vue b/apps/web-antd/src/views/system/dict/data/index.vue new file mode 100644 index 0000000..bb96f1f --- /dev/null +++ b/apps/web-antd/src/views/system/dict/data/index.vue @@ -0,0 +1,185 @@ + + + diff --git a/apps/web-antd/src/views/system/dict/data/tag-style-picker.vue b/apps/web-antd/src/views/system/dict/data/tag-style-picker.vue new file mode 100644 index 0000000..ac41b64 --- /dev/null +++ b/apps/web-antd/src/views/system/dict/data/tag-style-picker.vue @@ -0,0 +1,83 @@ + + + diff --git a/apps/web-antd/src/views/system/dict/index.vue b/apps/web-antd/src/views/system/dict/index.vue new file mode 100644 index 0000000..dcbfdd3 --- /dev/null +++ b/apps/web-antd/src/views/system/dict/index.vue @@ -0,0 +1,28 @@ + + + + diff --git a/apps/web-antd/src/views/system/dict/mitt.ts b/apps/web-antd/src/views/system/dict/mitt.ts new file mode 100644 index 0000000..f81c6f5 --- /dev/null +++ b/apps/web-antd/src/views/system/dict/mitt.ts @@ -0,0 +1,10 @@ +import { mitt } from '@vben/utils'; + +/** + * dictType: string + */ +type Events = { + rowClick: string; +}; + +export const emitter = mitt(); diff --git a/apps/web-antd/src/views/system/dict/type/data.ts b/apps/web-antd/src/views/system/dict/type/data.ts new file mode 100644 index 0000000..3a0977f --- /dev/null +++ b/apps/web-antd/src/views/system/dict/type/data.ts @@ -0,0 +1,77 @@ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +import { z } from '#/adapter/form'; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'dictName', + label: '字典名称', + }, + { + component: 'Input', + fieldName: 'dictType', + label: '字典类型', + }, +]; + +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + { + title: '字典名称', + field: 'dictName', + }, + { + title: '字典类型', + field: 'dictType', + }, + { + title: '备注', + field: 'remark', + }, + { + title: '创建时间', + field: 'createTime', + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 180, + }, +]; + +export const modalSchema: FormSchemaGetter = () => [ + { + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'dictId', + label: 'dictId', + }, + { + component: 'Input', + fieldName: 'dictName', + label: '字典名称', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'dictType', + // help: '使用英文/下划线命名, 如:sys_normal_disable', + label: '字典类型', + rules: z + .string() + .regex(/^[a-z_]+$/i, { message: '字典类型只能使用英文/下划线命名' }), + }, + { + component: 'Textarea', + fieldName: 'remark', + formItemClass: 'items-baseline', + label: '备注', + }, +]; diff --git a/apps/web-antd/src/views/system/dict/type/dict-type-modal.vue b/apps/web-antd/src/views/system/dict/type/dict-type-modal.vue new file mode 100644 index 0000000..7020b9e --- /dev/null +++ b/apps/web-antd/src/views/system/dict/type/dict-type-modal.vue @@ -0,0 +1,86 @@ + + + + diff --git a/apps/web-antd/src/views/system/dict/type/index.vue b/apps/web-antd/src/views/system/dict/type/index.vue new file mode 100644 index 0000000..912973f --- /dev/null +++ b/apps/web-antd/src/views/system/dict/type/index.vue @@ -0,0 +1,204 @@ + + + + diff --git a/apps/web-antd/src/views/system/menu/data.tsx b/apps/web-antd/src/views/system/menu/data.tsx new file mode 100644 index 0000000..bd4dbc3 --- /dev/null +++ b/apps/web-antd/src/views/system/menu/data.tsx @@ -0,0 +1,392 @@ +/* + * @Author: Mm + * @Date: 2025-03-10 10:03:34 + * @LastEditors: Mm + * @LastEditTime: 2025-03-13 17:27:00 + * 不写bug的程序员不是一个好程序员! + */ + +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +import { h } from 'vue'; + +import { DictEnum } from '@vben/constants'; +import { FolderIcon, MenuIcon, OkButtonIcon, VbenIcon } from '@vben/icons'; +import { $t } from '@vben/locales'; +import { getPopupContainer } from '@vben/utils'; + +import { z } from '#/adapter/form'; +import { getDictOptions } from '#/utils/dict'; +import { renderDict } from '#/utils/render'; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'menuName', + label: '菜单名称 ', + }, + { + component: 'Select', + componentProps: { + getPopupContainer, + options: getDictOptions(DictEnum.SYS_NORMAL_DISABLE), + }, + fieldName: 'status', + label: '菜单状态 ', + }, + { + component: 'Select', + componentProps: { + getPopupContainer, + options: getDictOptions(DictEnum.SYS_SHOW_HIDE), + }, + fieldName: 'visible', + label: '显示状态', + }, +]; + +// 菜单类型(M目录 C菜单 F按钮) +export const menuTypeOptions = [ + { label: '目录', value: 'M' }, + { label: '菜单', value: 'C' }, + { label: '按钮', value: 'F' }, +]; + +export const yesNoOptions = [ + { label: '是', value: '0' }, + { label: '否', value: '1' }, +]; + +// (M目录 C菜单 F按钮) +const menuTypes = { + C: { icon: MenuIcon, value: '菜单' }, + F: { icon: OkButtonIcon, value: '按钮' }, + M: { icon: FolderIcon, value: '目录' }, +}; +export const columns: VxeGridProps['columns'] = [ + { + title: '菜单名称', + field: 'menuName', + treeNode: true, + width: 200, + slots: { + // 需要i18n支持 否则返回原始值 + default: ({ row }) => $t(row.menuName), + }, + }, + { + title: '图标', + field: 'icon', + width: 80, + slots: { + default: ({ row }) => { + if (row?.icon === '#') { + return ''; + } + return ( + + + + ); + }, + }, + }, + { + title: '排序', + field: 'orderNum', + width: 120, + }, + { + title: '组件类型', + field: 'menuType', + width: 150, + slots: { + default: ({ row }) => { + const current = menuTypes[row.menuType as 'C' | 'F' | 'M']; + if (!current) { + return '未知'; + } + return ( + + {h(current.icon, { class: 'size-[18px]' })} + {current.value} + + ); + }, + }, + }, + { + title: '权限标识', + field: 'perms', + }, + { + title: '组件路径', + field: 'component', + }, + { + title: '状态', + field: 'status', + width: 100, + slots: { + default: ({ row }) => { + return renderDict(row.status, DictEnum.SYS_NORMAL_DISABLE); + }, + }, + }, + { + title: '显示', + field: 'visible', + width: 100, + slots: { + default: ({ row }) => { + return renderDict(row.visible, DictEnum.SYS_SHOW_HIDE); + }, + }, + }, + { + title: '创建时间', + field: 'createTime', + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 200, + }, +]; + +export const modalSchema: FormSchemaGetter = () => [ + { + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'menuId', + }, + { + component: 'TreeSelect', + defaultValue: 0, + fieldName: 'parentId', + label: '上级菜单', + rules: 'selectRequired', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: menuTypeOptions, + optionType: 'button', + }, + defaultValue: 'M', + dependencies: { + componentProps: (_, api) => { + // 切换时清空校验 + // 直接抄的源码 没有清空校验的方法 + Object.keys(api.errors.value).forEach((key) => { + api.setFieldError(key, undefined); + }); + return {}; + }, + triggerFields: ['menuType'], + }, + fieldName: 'menuType', + label: '菜单类型', + }, + { + component: 'Input', + dependencies: { + // 类型不为按钮时显示 + show: (values) => values.menuType !== 'F', + triggerFields: ['menuType'], + }, + renderComponentContent: (model) => ({ + addonBefore: () => , + addonAfter: () => ( + + 搜索图标 + + ), + }), + fieldName: 'icon', + help: '点击搜索图标跳转到iconify & 粘贴', + label: '菜单图标', + }, + { + component: 'Input', + fieldName: 'menuName', + label: '菜单名称', + help: '支持i18n写法, 如: menu.system.user', + rules: 'required', + }, + { + component: 'InputNumber', + fieldName: 'orderNum', + help: '排序, 数字越小越靠前', + label: '显示排序', + rules: 'required', + }, + { + component: 'Input', + componentProps: (model) => { + const placeholder = + model.isFrame === '0' + ? '填写链接地址http(s):// 使用新页面打开' + : '填写`路由地址`或者`链接地址` 链接默认使用内部iframe内嵌打开'; + return { + placeholder, + }; + }, + dependencies: { + rules: (model) => { + if (model.isFrame !== '0') { + return z + .string({ message: '请输入路由地址' }) + .refine((val) => !val.startsWith('/'), { + message: '路由地址不需要带/', + }); + } + // 为链接 + return z + .string({ message: '请输入链接地址' }) + .regex(/^https?:\/\//, { message: '请输入正确的链接地址' }); + }, + // 类型不为按钮时显示 + show: (values) => values?.menuType !== 'F', + triggerFields: ['isFrame', 'menuType'], + }, + fieldName: 'path', + help: `路由地址不带/, 如: menu, user\n 链接为http(s)://开头\n 链接默认使用内部iframe打开, 可通过{是否外链}控制打开方式`, + label: '路由地址', + }, + { + component: 'Input', + componentProps: (model) => { + return { + // 为链接时组件disabled + disabled: model.isFrame === '0', + }; + }, + defaultValue: '', + dependencies: { + rules: (model) => { + // 非链接时为必填项 + if (model.path && !/^https?:\/\//.test(model.path)) { + return z + .string() + .min(1, { message: '非链接时必填组件路径' }) + .refine((val) => !val.startsWith('/') && !val.endsWith('/'), { + message: '组件路径开头/末尾不需要带/', + }); + } + // 为链接时非必填 + return z.string().optional(); + }, + // 类型为菜单时显示 + show: (values) => values.menuType === 'C', + triggerFields: ['menuType', 'path'], + }, + fieldName: 'component', + help: '填写./src/views下的组件路径, 如system/menu/index', + label: '组件路径', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: yesNoOptions, + optionType: 'button', + }, + defaultValue: '1', + dependencies: { + // 类型不为按钮时显示 + show: (values) => values.menuType !== 'F', + triggerFields: ['menuType'], + }, + fieldName: 'isFrame', + help: '外链为http(s)://开头\n 选择否时, 使用iframe从内部打开页面, 否则新窗口打开', + label: '是否外链', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: getDictOptions(DictEnum.SYS_SHOW_HIDE), + optionType: 'button', + }, + defaultValue: '0', + dependencies: { + // 类型不为按钮时显示 + show: (values) => values.menuType !== 'F', + triggerFields: ['menuType'], + }, + fieldName: 'visible', + help: '隐藏后不会出现在菜单栏, 但仍然可以访问', + label: '是否显示', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: getDictOptions(DictEnum.SYS_NORMAL_DISABLE), + optionType: 'button', + }, + defaultValue: '0', + dependencies: { + // 类型不为按钮时显示 + show: (values) => values.menuType !== 'F', + triggerFields: ['menuType'], + }, + fieldName: 'status', + help: '停用后不会出现在菜单栏, 也无法访问', + label: '菜单状态', + }, + { + component: 'Input', + dependencies: { + // 类型为菜单/按钮时显示 + show: (values) => values.menuType !== 'M', + triggerFields: ['menuType'], + }, + fieldName: 'perms', + help: `控制器中定义的权限字符\n 如: @SaCheckPermission("system:user:import")`, + label: '权限标识', + }, + { + component: 'Input', + componentProps: (model) => ({ + // 为链接时组件disabled + disabled: model.isFrame === '0', + placeholder: '必须为json字符串格式', + }), + dependencies: { + // 类型为菜单时显示 + show: (values) => values.menuType === 'C', + triggerFields: ['menuType'], + }, + fieldName: 'queryParam', + help: 'vue-router中的query属性\n 如{"name": "xxx", "age": 16}', + label: '路由参数', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: yesNoOptions, + optionType: 'button', + }, + defaultValue: '0', + dependencies: { + // 类型为菜单时显示 + show: (values) => values.menuType === 'C', + triggerFields: ['menuType'], + }, + fieldName: 'isCache', + help: '路由的keepAlive属性', + label: '是否缓存', + }, +]; + + diff --git a/apps/web-antd/src/views/system/menu/index.vue b/apps/web-antd/src/views/system/menu/index.vue new file mode 100644 index 0000000..589c030 --- /dev/null +++ b/apps/web-antd/src/views/system/menu/index.vue @@ -0,0 +1,204 @@ + + + + diff --git a/apps/web-antd/src/views/system/menu/menu-modal.vue b/apps/web-antd/src/views/system/menu/menu-modal.vue new file mode 100644 index 0000000..ec2bc00 --- /dev/null +++ b/apps/web-antd/src/views/system/menu/menu-modal.vue @@ -0,0 +1,150 @@ + + + + diff --git a/apps/web-antd/src/views/system/notice/data.ts b/apps/web-antd/src/views/system/notice/data.ts new file mode 100644 index 0000000..4e463e9 --- /dev/null +++ b/apps/web-antd/src/views/system/notice/data.ts @@ -0,0 +1,127 @@ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +import { DictEnum } from '@vben/constants'; +import { getPopupContainer } from '@vben/utils'; + +import { getDictOptions } from '#/utils/dict'; +import { renderDict } from '#/utils/render'; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'noticeTitle', + label: '公告标题', + }, + { + component: 'Input', + fieldName: 'createBy', + label: '创建人', + }, + { + component: 'Select', + componentProps: { + getPopupContainer, + options: getDictOptions(DictEnum.SYS_NOTICE_TYPE), + }, + fieldName: 'noticeType', + label: '公告类型', + }, +]; + +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + { + title: '公告标题', + field: 'noticeTitle', + }, + { + title: '公告类型', + field: 'noticeType', + width: 120, + slots: { + default: ({ row }) => { + return renderDict(row.noticeType, DictEnum.SYS_NOTICE_TYPE); + }, + }, + }, + { + title: '状态', + field: 'status', + width: 120, + slots: { + default: ({ row }) => { + return renderDict(row.status, DictEnum.SYS_NOTICE_STATUS); + }, + }, + }, + { + title: '创建人', + field: 'createByName', + width: 150, + }, + { + title: '创建时间', + field: 'createTime', + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 180, + }, +]; + +export const modalSchema: FormSchemaGetter = () => [ + { + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'noticeId', + label: '主键', + }, + { + component: 'Input', + fieldName: 'noticeTitle', + label: '公告标题', + rules: 'required', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: getDictOptions(DictEnum.SYS_NOTICE_STATUS), + optionType: 'button', + }, + defaultValue: '0', + fieldName: 'status', + label: '公告状态', + rules: 'required', + formItemClass: 'col-span-1', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: getDictOptions(DictEnum.SYS_NOTICE_TYPE), + optionType: 'button', + }, + defaultValue: '1', + fieldName: 'noticeType', + label: '公告类型', + rules: 'required', + formItemClass: 'col-span-1', + }, + { + component: 'RichTextarea', + componentProps: { + width: '100%', + }, + fieldName: 'noticeContent', + label: '公告内容', + rules: 'required', + }, +]; diff --git a/apps/web-antd/src/views/system/notice/index.vue b/apps/web-antd/src/views/system/notice/index.vue new file mode 100644 index 0000000..dd44356 --- /dev/null +++ b/apps/web-antd/src/views/system/notice/index.vue @@ -0,0 +1,148 @@ + + + diff --git a/apps/web-antd/src/views/system/notice/notice-modal.vue b/apps/web-antd/src/views/system/notice/notice-modal.vue new file mode 100644 index 0000000..ab1255f --- /dev/null +++ b/apps/web-antd/src/views/system/notice/notice-modal.vue @@ -0,0 +1,79 @@ + + + diff --git a/apps/web-antd/src/views/system/oss-config/data.tsx b/apps/web-antd/src/views/system/oss-config/data.tsx new file mode 100644 index 0000000..0143969 --- /dev/null +++ b/apps/web-antd/src/views/system/oss-config/data.tsx @@ -0,0 +1,352 @@ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +import { DictEnum } from '@vben/constants'; + +import { Tag } from 'ant-design-vue'; + +import { z } from '#/adapter/form'; +import { getDictOptions } from '#/utils/dict'; + +const accessPolicyOptions = [ + { color: 'orange', label: '私有', value: '0' }, + { color: 'green', label: '公开', value: '1' }, + { color: 'blue', label: '自定义', value: '2' }, +]; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'configKey', + label: '配置名称', + }, + { + component: 'Input', + fieldName: 'bucketName', + label: '桶名称', + }, + { + component: 'Select', + componentProps: { + options: getDictOptions(DictEnum.SYS_YES_NO), + }, + fieldName: 'status', + label: '是否默认', + }, +]; + +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + { + title: '配置名称', + field: 'configKey', + }, + { + title: '访问站点', + field: 'endpoint', + showOverflow: true, + }, + { + title: '桶名称', + field: 'bucketName', + }, + { + title: '域', + field: 'region', + }, + { + title: '权限桶类型', + field: 'accessPolicy', + slots: { + default: ({ row }) => { + const current = accessPolicyOptions.find( + (item) => item.value === row.accessPolicy, + ); + if (current) { + return {current.label}; + } + return '未知类型'; + }, + }, + }, + { + title: '是否默认', + field: 'status', + slots: { + default: 'status', + }, + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 180, + }, +]; + +export const drawerSchema: FormSchemaGetter = () => [ + { + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'ossConfigId', + }, + { + component: 'Divider', + componentProps: { + orientation: 'center', + }, + fieldName: 'divider1', + hideLabel: true, + renderComponentContent: () => ({ + default: () => '基本信息', + }), + }, + { + component: 'Input', + fieldName: 'configKey', + label: '配置名称', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'endpoint', + label: '服务地址', + renderComponentContent: (formModel) => ({ + addonBefore: () => (formModel.isHttps === 'Y' ? 'https://' : 'http://'), + }), + rules: z + .string() + .refine((domain) => domain && !/^https?:\/\/.*/.test(domain), { + message: '请输入正确的域名, 不需要http(s)', + }), + }, + { + component: 'Input', + fieldName: 'domain', + label: '自定义域名', + }, + { + component: 'Input', + fieldName: 'tip', + label: '占位作为提示使用', + hideLabel: true, + }, + { + component: 'Divider', + componentProps: { + orientation: 'center', + }, + fieldName: 'divider2', + hideLabel: true, + renderComponentContent: () => ({ + default: () => '认证信息', + }), + }, + { + component: 'Input', + fieldName: 'accessKey', + label: 'accessKey', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'secretKey', + label: 'secretKey', + rules: 'required', + }, + { + component: 'Divider', + componentProps: { + orientation: 'center', + }, + fieldName: 'divider3', + hideLabel: true, + renderComponentContent: () => ({ + default: () => '其他信息', + }), + }, + { + component: 'Input', + fieldName: 'bucketName', + label: '桶名称', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'prefix', + label: '前缀', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: accessPolicyOptions, + optionType: 'button', + }, + defaultValue: '0', + fieldName: 'accessPolicy', + formItemClass: 'col-span-3 lg:col-span-2', + label: '权限桶类型', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: getDictOptions(DictEnum.SYS_YES_NO), + optionType: 'button', + }, + defaultValue: 'N', + fieldName: 'isHttps', + formItemClass: 'col-span-3 lg:col-span-1', + label: '是否https', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'region', + label: '区域', + }, + { + component: 'Textarea', + fieldName: 'remark', + formItemClass: 'items-baseline', + label: '备注', + }, +]; + +export const modalSchema: FormSchemaGetter = () => [ + { + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'ossConfigId', + }, + { + component: 'Divider', + componentProps: { + orientation: 'center', + }, + fieldName: 'divider1', + hideLabel: true, + renderComponentContent: () => ({ + default: () => '基本信息', + }), + }, + { + component: 'Input', + fieldName: 'configKey', + label: '配置名称', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'endpoint', + label: '服务地址', + renderComponentContent: (formModel) => ({ + addonBefore: () => (formModel.isHttps === 'Y' ? 'https://' : 'http://'), + }), + rules: z + .string() + .refine((domain) => domain && !/^https?:\/\/.*/.test(domain), { + message: '请输入正确的域名, 不需要http(s)', + }), + }, + { + component: 'Input', + fieldName: 'domain', + label: '自定义域名', + }, + { + component: 'Input', + fieldName: 'tip', + label: '占位作为提示使用', + hideLabel: true, + }, + { + component: 'Divider', + componentProps: { + orientation: 'center', + }, + fieldName: 'divider2', + hideLabel: true, + renderComponentContent: () => ({ + default: () => '认证信息', + }), + }, + { + component: 'Input', + fieldName: 'accessKey', + label: 'accessKey', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'secretKey', + label: 'secretKey', + rules: 'required', + }, + { + component: 'Divider', + componentProps: { + orientation: 'center', + }, + fieldName: 'divider3', + hideLabel: true, + renderComponentContent: () => ({ + default: () => '其他信息', + }), + }, + { + component: 'Input', + fieldName: 'bucketName', + label: '桶名称', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'prefix', + label: '前缀', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: accessPolicyOptions, + optionType: 'button', + }, + defaultValue: '0', + fieldName: 'accessPolicy', + formItemClass: 'col-span-3 lg:col-span-2', + label: '权限桶类型', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: getDictOptions(DictEnum.SYS_YES_NO), + optionType: 'button', + }, + defaultValue: 'N', + fieldName: 'isHttps', + formItemClass: 'col-span-3 lg:col-span-1', + label: '是否https', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'region', + label: '区域', + }, + { + component: 'Textarea', + fieldName: 'remark', + formItemClass: 'items-baseline', + label: '备注', + }, +]; diff --git a/apps/web-antd/src/views/system/oss-config/index.vue b/apps/web-antd/src/views/system/oss-config/index.vue new file mode 100644 index 0000000..12a16fc --- /dev/null +++ b/apps/web-antd/src/views/system/oss-config/index.vue @@ -0,0 +1,164 @@ + + + diff --git a/apps/web-antd/src/views/system/oss-config/oss-config-drawer.vue b/apps/web-antd/src/views/system/oss-config/oss-config-drawer.vue new file mode 100644 index 0000000..ddd194e --- /dev/null +++ b/apps/web-antd/src/views/system/oss-config/oss-config-drawer.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/apps/web-antd/src/views/system/oss-config/oss-config-modal.vue b/apps/web-antd/src/views/system/oss-config/oss-config-modal.vue new file mode 100644 index 0000000..6cb2935 --- /dev/null +++ b/apps/web-antd/src/views/system/oss-config/oss-config-modal.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/apps/web-antd/src/views/system/oss/data.tsx b/apps/web-antd/src/views/system/oss/data.tsx new file mode 100644 index 0000000..06d1bc6 --- /dev/null +++ b/apps/web-antd/src/views/system/oss/data.tsx @@ -0,0 +1,80 @@ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'fileName', + label: '文件名', + }, + { + component: 'Input', + fieldName: 'originalName', + label: '原名', + }, + { + component: 'Input', + fieldName: 'fileSuffix', + label: '拓展名', + }, + { + component: 'Input', + fieldName: 'service', + label: '服务商', + }, + { + component: 'RangePicker', + fieldName: 'createTime', + label: '创建时间', + }, +]; + +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + { + title: '文件名', + field: 'fileName', + showOverflow: true, + }, + { + title: '文件原名', + field: 'originalName', + showOverflow: true, + }, + { + title: '文件拓展名', + field: 'fileSuffix', + }, + { + title: '文件预览', + field: 'url', + showOverflow: true, + slots: { default: 'url' }, + }, + { + title: '创建时间', + field: 'createTime', + sortable: true, + }, + { + title: '上传人', + field: 'createByName', + }, + { + title: '服务商', + field: 'service', + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 180, + }, +]; + +/** + * 图片加载失败的fallback + */ +export const fallbackImageBase64 = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg=='; diff --git a/apps/web-antd/src/views/system/oss/file-upload-modal.vue b/apps/web-antd/src/views/system/oss/file-upload-modal.vue new file mode 100644 index 0000000..91a9549 --- /dev/null +++ b/apps/web-antd/src/views/system/oss/file-upload-modal.vue @@ -0,0 +1,52 @@ + + + diff --git a/apps/web-antd/src/views/system/oss/image-upload-modal.vue b/apps/web-antd/src/views/system/oss/image-upload-modal.vue new file mode 100644 index 0000000..87f0145 --- /dev/null +++ b/apps/web-antd/src/views/system/oss/image-upload-modal.vue @@ -0,0 +1,51 @@ + + + diff --git a/apps/web-antd/src/views/system/oss/index.vue b/apps/web-antd/src/views/system/oss/index.vue new file mode 100644 index 0000000..381ad6c --- /dev/null +++ b/apps/web-antd/src/views/system/oss/index.vue @@ -0,0 +1,258 @@ + + + diff --git a/apps/web-antd/src/views/system/post/data.ts b/apps/web-antd/src/views/system/post/data.ts new file mode 100644 index 0000000..7b2860a --- /dev/null +++ b/apps/web-antd/src/views/system/post/data.ts @@ -0,0 +1,141 @@ +/* + * @Author: Mm + * @Date: 2025-03-10 10:03:34 + * @LastEditors: Mm + * @LastEditTime: 2025-03-10 14:28:00 + * 不写bug的程序员不是一个好程序员! + */ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +import { DictEnum } from '@vben/constants'; +import { getPopupContainer } from '@vben/utils'; + +import { getDictOptions } from '#/utils/dict'; +import { renderDict } from '#/utils/render'; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'postCode', + label: '岗位编码', + }, + { + component: 'Input', + fieldName: 'postName', + label: '岗位名称', + }, + { + component: 'Select', + componentProps: { + getPopupContainer, + options: getDictOptions(DictEnum.SYS_NORMAL_DISABLE), + }, + fieldName: 'status', + label: '状态', + }, +]; + +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + { + title: '岗位编码', + field: 'postCode', + }, + // { + // title: '类别编码', + // field: 'postCategory', + // }, + { + title: '岗位名称', + field: 'postName', + }, + { + title: '排序', + field: 'postSort', + }, + { + title: '状态', + field: 'status', + slots: { + default: ({ row }) => { + return renderDict(row.status, DictEnum.SYS_NORMAL_DISABLE); + }, + }, + }, + { + title: '创建时间', + field: 'createTime', + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 180, + }, +]; + +export const modalSchema: FormSchemaGetter = () => [ + { + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'postId', + label: 'postId', + }, + { + component: 'TreeSelect', + componentProps: { + getPopupContainer, + }, + fieldName: 'deptId', + label: '所属部门', + rules: 'selectRequired', + }, + { + component: 'Input', + fieldName: 'postName', + label: '岗位名称', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'postCode', + label: '岗位编码', + rules: 'required', + }, + // { + // component: 'Input', + // fieldName: 'postCategory', + // label: '类别编码', + // }, + { + component: 'InputNumber', + fieldName: 'postSort', + label: '岗位排序', + rules: 'required', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: getDictOptions(DictEnum.SYS_NORMAL_DISABLE), + optionType: 'button', + }, + defaultValue: '0', + fieldName: 'status', + label: '岗位状态', + rules: 'required', + }, + { + component: 'Textarea', + fieldName: 'remark', + formItemClass: 'items-baseline', + label: '备注', + }, +]; + + diff --git a/apps/web-antd/src/views/system/post/index.vue b/apps/web-antd/src/views/system/post/index.vue new file mode 100644 index 0000000..8ab3536 --- /dev/null +++ b/apps/web-antd/src/views/system/post/index.vue @@ -0,0 +1,196 @@ + + + + diff --git a/apps/web-antd/src/views/system/post/post-modal.vue b/apps/web-antd/src/views/system/post/post-modal.vue new file mode 100644 index 0000000..2bce5d4 --- /dev/null +++ b/apps/web-antd/src/views/system/post/post-modal.vue @@ -0,0 +1,109 @@ + + + + diff --git a/apps/web-antd/src/views/system/role-assign/data.tsx b/apps/web-antd/src/views/system/role-assign/data.tsx new file mode 100644 index 0000000..75f8bfe --- /dev/null +++ b/apps/web-antd/src/views/system/role-assign/data.tsx @@ -0,0 +1,42 @@ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'userName', + label: '用户账号', + }, + { + component: 'Input', + fieldName: 'phonenumber', + label: '手机号码', + }, +]; + +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + { + title: '用户账号', + field: 'userName', + }, + { + title: '用户昵称', + field: 'nickName', + }, + { + title: '邮箱', + field: 'email', + }, + { + title: '手机号', + field: 'phonenumber', + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 180, + }, +]; diff --git a/apps/web-antd/src/views/system/role-assign/index.vue b/apps/web-antd/src/views/system/role-assign/index.vue new file mode 100644 index 0000000..57965ee --- /dev/null +++ b/apps/web-antd/src/views/system/role-assign/index.vue @@ -0,0 +1,152 @@ + + + diff --git a/apps/web-antd/src/views/system/role-assign/role-assign-drawer.vue b/apps/web-antd/src/views/system/role-assign/role-assign-drawer.vue new file mode 100644 index 0000000..74e3e91 --- /dev/null +++ b/apps/web-antd/src/views/system/role-assign/role-assign-drawer.vue @@ -0,0 +1,87 @@ + + + diff --git a/apps/web-antd/src/views/system/role/data.tsx b/apps/web-antd/src/views/system/role/data.tsx new file mode 100644 index 0000000..1fcff12 --- /dev/null +++ b/apps/web-antd/src/views/system/role/data.tsx @@ -0,0 +1,233 @@ +/* + * @Author: Mm + * @Date: 2025-02-20 09:55:11 + * @LastEditors: Mm + * @LastEditTime: 2025-03-13 17:26:10 + * 不写bug的程序员不是一个好程序员! + */ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +import { DictEnum } from '@vben/constants'; +import { getPopupContainer } from '@vben/utils'; + +import { Tag } from 'ant-design-vue'; + +import { getDictOptions } from '#/utils/dict'; + +/** + * authScopeOptions user也会用到 + */ +export const authScopeOptions = [ + { color: 'green', label: '全部数据权限', value: '1' }, + { color: 'default', label: '自定数据权限', value: '2' }, + { color: 'orange', label: '本部门数据权限', value: '3' }, + { color: 'cyan', label: '本部门及以下数据权限', value: '4' }, + { color: 'error', label: '仅本人数据权限', value: '5' }, + { color: 'default', label: '部门及以下或本人数据权限', value: '6' }, +]; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'roleName', + label: '角色名称', + }, + { + component: 'Input', + fieldName: 'roleKey', + label: '权限字符', + }, + { + component: 'Select', + componentProps: { + options: getDictOptions(DictEnum.SYS_NORMAL_DISABLE), + }, + fieldName: 'status', + label: '状态', + }, + { + component: 'RangePicker', + fieldName: 'createTime', + label: '创建时间', + }, +]; + +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + { + title: '角色名称', + field: 'roleName', + }, + { + title: '权限字符', + field: 'roleKey', + slots: { + default: ({ row }) => { + return {row.roleKey}; + }, + }, + }, + { + title: '数据权限', + field: 'dataScope', + slots: { + default: ({ row }) => { + const found = authScopeOptions.find( + (item) => item.value === row.dataScope, + ); + if (found) { + return {found.label}; + } + return {row.dataScope}; + }, + }, + }, + { + title: '排序', + field: 'roleSort', + }, + { + title: '状态', + field: 'status', + slots: { default: 'status' }, + }, + { + title: '创建时间', + field: 'createTime', + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 180, + }, +]; + +export const modalSchema: FormSchemaGetter = () => [ + { + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'roleId', + label: '角色ID', + }, + { + component: 'Input', + fieldName: 'roleName', + label: '角色名称', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'roleKey', + help: '如: test simpleUser等', + label: '权限标识', + rules: 'required', + }, + { + component: 'InputNumber', + fieldName: 'roleSort', + label: '角色排序', + rules: 'required', + }, + { + component: 'Select', + componentProps: { + allowClear: false, + options: getDictOptions(DictEnum.SYS_NORMAL_DISABLE), + getPopupContainer, + }, + defaultValue: '0', + fieldName: 'status', + help: '修改后, 拥有该角色的用户将自动下线.', + label: '角色状态', + rules: 'required', + }, + { + component: 'Radio', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'menuCheckStrictly', + label: '菜单权限', + }, + { + component: 'Input', + defaultValue: [], + fieldName: 'menuIds', + label: '菜单权限', + formItemClass: 'col-span-2', + }, + { + component: 'Textarea', + defaultValue: '', + fieldName: 'remark', + formItemClass: 'items-baseline col-span-2', + label: '备注', + }, +]; + +export const authModalSchemas: FormSchemaGetter = () => [ + { + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'roleId', + label: '角色ID', + }, + { + component: 'Radio', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'deptCheckStrictly', + label: 'deptCheckStrictly', + }, + { + component: 'Input', + componentProps: { + disabled: true, + }, + fieldName: 'roleName', + label: '角色名称', + }, + { + component: 'Input', + componentProps: { + disabled: true, + }, + fieldName: 'roleKey', + label: '权限标识', + }, + { + component: 'Select', + componentProps: { + allowClear: false, + getPopupContainer, + options: authScopeOptions, + }, + fieldName: 'dataScope', + help: '更改后需要用户重新登录才能生效', + label: '权限范围', + }, + { + component: 'TreeSelect', + defaultValue: [], + dependencies: { + show: (values) => values.dataScope === '2', + triggerFields: ['dataScope'], + }, + fieldName: 'deptIds', + formItemClass: 'items-baseline', + help: '更改后立即生效', + label: '部门权限', + }, +]; diff --git a/apps/web-antd/src/views/system/role/index.vue b/apps/web-antd/src/views/system/role/index.vue new file mode 100644 index 0000000..0656aaa --- /dev/null +++ b/apps/web-antd/src/views/system/role/index.vue @@ -0,0 +1,253 @@ + + + + diff --git a/apps/web-antd/src/views/system/role/role-auth-modal.vue b/apps/web-antd/src/views/system/role/role-auth-modal.vue new file mode 100644 index 0000000..00d727c --- /dev/null +++ b/apps/web-antd/src/views/system/role/role-auth-modal.vue @@ -0,0 +1,127 @@ + + + + diff --git a/apps/web-antd/src/views/system/role/role-modal.vue b/apps/web-antd/src/views/system/role/role-modal.vue new file mode 100644 index 0000000..c3c9ad6 --- /dev/null +++ b/apps/web-antd/src/views/system/role/role-modal.vue @@ -0,0 +1,141 @@ + + + diff --git a/apps/web-antd/src/views/system/tenant/data.tsx b/apps/web-antd/src/views/system/tenant/data.tsx new file mode 100644 index 0000000..1d47f0d --- /dev/null +++ b/apps/web-antd/src/views/system/tenant/data.tsx @@ -0,0 +1,267 @@ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +import { getPopupContainer } from '@vben/utils'; + +import dayjs from 'dayjs'; + +import { z } from '#/adapter/form'; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'tenantId', + label: '租户编号', + }, + { + component: 'Input', + fieldName: 'companyName', + label: '租户名称', + }, + { + component: 'Input', + fieldName: 'contactUserName', + label: '联系人', + }, + { + component: 'Input', + fieldName: 'contactPhone', + label: '联系电话', + }, +]; + +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + { + title: '租户编号', + field: 'tenantId', + }, + { + title: '租户名称', + field: 'companyName', + }, + { + title: '联系人', + field: 'contactUserName', + }, + { + title: '联系电话', + field: 'contactPhone', + }, + { + title: '到期时间', + field: 'expireTime', + formatter: ({ cellValue }) => { + if (!cellValue) { + return '无期限'; + } + return cellValue; + }, + }, + { + title: '租户状态', + field: 'status', + slots: { default: 'status' }, + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 200, + }, +]; + +const defaultExpireTime = dayjs() + .add(365, 'days') + .startOf('day') + .format('YYYY-MM-DD HH:mm:ss'); + +export const drawerSchema: FormSchemaGetter = () => [ + { + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'id', + label: 'id', + }, + { + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'tenantId', + label: 'tenantId', + }, + { + component: 'Divider', + componentProps: { + orientation: 'center', + }, + fieldName: 'divider1', + hideLabel: true, + renderComponentContent: () => ({ + default: () => '基本信息', + }), + }, + { + component: 'Input', + fieldName: 'companyName', + label: '企业名称', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'contactUserName', + label: '联系人', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'contactPhone', + label: '联系电话', + rules: z + .string() + .regex(/^1[3-9]\d{9}$/, { message: '请输入正确的联系电话' }), + }, + { + component: 'Divider', + componentProps: { + orientation: 'center', + }, + fieldName: 'divider2', + hideLabel: true, + renderComponentContent: () => ({ + default: () => '管理员信息', + }), + dependencies: { + if: (values) => !values?.tenantId, + triggerFields: ['tenantId'], + }, + }, + { + component: 'Input', + fieldName: 'username', + label: '用户账号', + rules: 'required', + dependencies: { + if: (values) => !values?.tenantId, + triggerFields: ['tenantId'], + }, + }, + { + component: 'InputPassword', + fieldName: 'password', + label: '用户密码', + rules: 'required', + dependencies: { + if: (values) => !values?.tenantId, + triggerFields: ['tenantId'], + }, + }, + { + component: 'Divider', + componentProps: { + orientation: 'center', + }, + fieldName: 'divider3', + hideLabel: true, + renderComponentContent: () => ({ + default: () => '租户设置', + }), + }, + { + component: 'Select', + componentProps: { + getPopupContainer, + }, + fieldName: 'packageId', + label: '租户套餐', + rules: 'selectRequired', + }, + { + component: 'DatePicker', + componentProps: { + format: 'YYYY-MM-DD HH:mm:ss', + showTime: true, + valueFormat: 'YYYY-MM-DD HH:mm:ss', + getPopupContainer, + }, + defaultValue: defaultExpireTime, + fieldName: 'expireTime', + help: `已经设置过期时间不允许重置为'无期限'\n即在开通时未设置无期限 以后都不允许设置`, + label: '过期时间', + }, + { + component: 'InputNumber', + componentProps: { + min: -1, + }, + defaultValue: -1, + fieldName: 'accountCount', + help: '-1不限制用户数量', + label: '用户数量', + renderComponentContent(model) { + return { + addonBefore: () => + model.accountCount === -1 ? '不限制数量' : '输入数量', + }; + }, + rules: 'required', + }, + { + component: 'Input', + fieldName: 'domain', + help: '可填写域名/端口 填写域名如: www.test.com 或者 www.test.com:8080 填写ip:端口如: 127.0.0.1:8080', + label: '绑定域名', + renderComponentContent() { + return { + addonBefore: () => 'http(s)://', + }; + }, + rules: z + .string() + .refine( + (domain) => + !(domain.startsWith('http://') || domain.startsWith('https://')), + { message: '请输入正确的域名, 不需要http(s)' }, + ) + .optional(), + }, + { + component: 'Divider', + componentProps: { + orientation: 'center', + }, + fieldName: 'divider4', + hideLabel: true, + renderComponentContent: () => ({ + default: () => '企业信息', + }), + }, + { + component: 'Input', + fieldName: 'address', + label: '企业地址', + }, + { + component: 'Input', + fieldName: 'licenseNumber', + label: '企业代码', + }, + { + component: 'Textarea', + fieldName: 'intro', + formItemClass: 'items-baseline', + label: '企业介绍', + }, + { + component: 'Textarea', + fieldName: 'remark', + formItemClass: 'items-baseline', + label: '备注', + }, +]; diff --git a/apps/web-antd/src/views/system/tenant/index.vue b/apps/web-antd/src/views/system/tenant/index.vue new file mode 100644 index 0000000..0166704 --- /dev/null +++ b/apps/web-antd/src/views/system/tenant/index.vue @@ -0,0 +1,233 @@ + + + diff --git a/apps/web-antd/src/views/system/tenant/tenant-drawer.vue b/apps/web-antd/src/views/system/tenant/tenant-drawer.vue new file mode 100644 index 0000000..679b876 --- /dev/null +++ b/apps/web-antd/src/views/system/tenant/tenant-drawer.vue @@ -0,0 +1,119 @@ + + + + + diff --git a/apps/web-antd/src/views/system/tenantPackage/data.ts b/apps/web-antd/src/views/system/tenantPackage/data.ts new file mode 100644 index 0000000..415d6c2 --- /dev/null +++ b/apps/web-antd/src/views/system/tenantPackage/data.ts @@ -0,0 +1,76 @@ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'packageName', + label: '套餐名称', + }, +]; + +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + { + title: '套餐名称', + field: 'packageName', + }, + { + title: '备注', + field: 'remark', + }, + { + title: '状态', + field: 'status', + slots: { default: 'status' }, + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 180, + }, +]; + +export const drawerSchema: FormSchemaGetter = () => [ + { + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'packageId', + }, + { + component: 'Radio', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'menuCheckStrictly', + }, + { + component: 'Input', + fieldName: 'packageName', + label: '套餐名称', + rules: 'required', + }, + { + component: 'menuIds', + defaultValue: [], + fieldName: 'menuIds', + label: '关联菜单', + }, + { + component: 'Textarea', + fieldName: 'remark', + formItemClass: 'items-baseline', + label: '备注', + }, +]; + +// 租户管理 不可分配 只有superadmin有权限操作 分配了也没用 +export const excludeIds = [ + 6, 121, 122, 1606, 1607, 1608, 1609, 1610, 1611, 1612, 1613, 1614, 1615, +]; diff --git a/apps/web-antd/src/views/system/tenantPackage/index.vue b/apps/web-antd/src/views/system/tenantPackage/index.vue new file mode 100644 index 0000000..4a6a6d3 --- /dev/null +++ b/apps/web-antd/src/views/system/tenantPackage/index.vue @@ -0,0 +1,191 @@ + + + diff --git a/apps/web-antd/src/views/system/tenantPackage/tenant-package-drawer.vue b/apps/web-antd/src/views/system/tenantPackage/tenant-package-drawer.vue new file mode 100644 index 0000000..839dbaa --- /dev/null +++ b/apps/web-antd/src/views/system/tenantPackage/tenant-package-drawer.vue @@ -0,0 +1,146 @@ + + + diff --git a/apps/web-antd/src/views/system/tenantPackage/tree-item.tsx b/apps/web-antd/src/views/system/tenantPackage/tree-item.tsx new file mode 100644 index 0000000..392943f --- /dev/null +++ b/apps/web-antd/src/views/system/tenantPackage/tree-item.tsx @@ -0,0 +1,44 @@ +import type { PropType } from 'vue'; + +import type { Menu } from '#/api/system/menu/model'; + +import { computed, defineComponent } from 'vue'; + +import { Tag } from 'ant-design-vue'; + +export default defineComponent({ + name: 'TreeItem', + props: { + data: { + required: true, + type: Object as PropType, + }, + }, + setup(props, { expose }) { + expose(); + + interface TagProp { + color: string; + text: string; + } + + const menuTagProp = computed(() => { + // 正则判断是否为链接 + if (/^https?:\/\/[^\s/$.?#].\S*$/i.test(props.data.path)) { + return { color: 'pink', text: '外链' }; + } + const type = props.data.menuType; + if (type === 'M') return { color: 'green', text: '目录' }; + if (type === 'C') return { color: 'blue', text: '菜单' }; + if (type === 'F') return { color: '', text: '按钮' }; + return { color: 'error', text: '未知' }; + }); + + return () => ( +
    + {props.data.menuName} + {menuTagProp.value.text} +
    + ); + }, +}); diff --git a/apps/web-antd/src/views/system/user/data.tsx b/apps/web-antd/src/views/system/user/data.tsx new file mode 100644 index 0000000..8c5acb8 --- /dev/null +++ b/apps/web-antd/src/views/system/user/data.tsx @@ -0,0 +1,350 @@ +/* + * @Author: Mm + * @Date: 2025-02-20 09:55:11 + * @LastEditors: hcc + * @LastEditTime: 2025-05-24 17:27:41 + * 不写bug的程序员不是一个好程序员! + */ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +import { DictEnum } from '@vben/constants'; +import { getPopupContainer } from '@vben/utils'; + +import { z } from '#/adapter/form'; +import { getDictOptions } from '#/utils/dict'; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'userName', + label: '用户账号', + }, + { + component: 'Input', + fieldName: 'nickName', + label: '用户昵称', + }, + { + component: 'Input', + fieldName: 'phonenumber', + label: '手机号码', + }, + { + component: 'Select', + componentProps: { + getPopupContainer, + options: getDictOptions(DictEnum.SYS_NORMAL_DISABLE), + }, + fieldName: 'status', + label: '用户状态', + }, + { + component: 'RangePicker', + fieldName: 'createTime', + label: '创建时间', + }, +]; + +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + { + field: 'userName', + title: '名称', + minWidth: 80, + }, + { + field: 'nickName', + title: '昵称', + minWidth: 130, + }, + { + field: 'avatar', + title: '头像', + slots: { default: 'avatar' }, + minWidth: 80, + }, + { + field: 'deptName', + title: '部门', + minWidth: 120, + }, + { + field: 'workDeptName', + title: '工作部门', + minWidth: 120, + }, + { + field: 'phonenumber', + title: '手机号', + formatter({ cellValue }) { + return cellValue || '暂无'; + }, + minWidth: 120, + }, + { + field: 'status', + title: '状态', + slots: { default: 'status' }, + minWidth: 100, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 150, + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + resizable: false, + width: 180, + }, +]; + +export const drawerSchema: FormSchemaGetter = () => [ + { + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'userId', + }, + { + component: 'Input', + fieldName: 'userName', + label: '用户账号', + rules: 'required', + }, + { + component: 'InputPassword', + fieldName: 'password', + label: '用户密码', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'nickName', + label: '用户昵称', + rules: 'required', + }, + { + component: 'TreeSelect', + // 在drawer里更新 这里不需要默认的componentProps + defaultValue: undefined, + fieldName: 'workDeptId', + label: '所属部门', + rules: 'selectRequired', + }, + { + component: 'TreeSelect', + // 在drawer里更新 这里不需要默认的componentProps + defaultValue: undefined, + fieldName: 'deptId', + label: '管理部门', + rules: 'selectRequired', + }, + { + component: 'Input', + fieldName: 'phonenumber', + label: '手机号码', + defaultValue: undefined, + rules: z + .string() + .regex(/^1[3-9]\d{9}$/, '请输入正确的手机号码') + .optional() + .or(z.literal('')), + }, + { + component: 'Input', + fieldName: 'email', + defaultValue: undefined, + label: '邮箱', + /** + * z.literal 是 Zod 中的一种类型,用于定义一个特定的字面量值。 + * 它可以用于确保输入的值与指定的字面量完全匹配。 + * 例如,你可以使用 z.literal 来确保某个字段的值只能是特定的字符串、数字、布尔值等。 + * 即空字符串也可通过校验 + */ + rules: z.string().email('请输入正确的邮箱').optional().or(z.literal('')), + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: getDictOptions(DictEnum.SYS_USER_SEX), + optionType: 'button', + }, + defaultValue: '0', + fieldName: 'sex', + formItemClass: 'col-span-2 lg:col-span-1', + label: '性别', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: getDictOptions(DictEnum.SYS_NORMAL_DISABLE), + optionType: 'button', + }, + defaultValue: '0', + fieldName: 'status', + formItemClass: 'col-span-2 lg:col-span-1', + label: '状态', + }, + { + component: 'Select', + componentProps: { + getPopupContainer, + mode: 'multiple', + optionFilterProp: 'label', + optionLabelProp: 'label', + placeholder: '请先选择部门', + }, + fieldName: 'postIds', + help: '选择部门后, 将自动加载该部门下所有的岗位', + label: '岗位', + }, + { + component: 'Select', + componentProps: { + getPopupContainer, + mode: 'multiple', + optionFilterProp: 'title', + optionLabelProp: 'title', + }, + fieldName: 'roleIds', + label: '角色', + }, + { + component: 'Textarea', + fieldName: 'remark', + formItemClass: 'items-baseline', + label: '备注', + }, +]; +export const modalSchema: FormSchemaGetter = () => [ + { + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'userId', + }, + { + component: 'Input', + fieldName: 'userName', + label: '用户账号', + rules: 'required', + }, + { + component: 'InputPassword', + fieldName: 'password', + label: '用户密码', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'nickName', + label: '用户昵称', + rules: 'required', + }, + { + component: 'TreeSelect', + // 在drawer里更新 这里不需要默认的componentProps + defaultValue: undefined, + fieldName: 'workDeptId', + label: '所属部门', + rules: 'selectRequired', + }, + { + component: 'TreeSelect', + // 在drawer里更新 这里不需要默认的componentProps + defaultValue: undefined, + fieldName: 'deptId', + label: '管理部门', + rules: 'selectRequired', + }, + + { + component: 'Input', + fieldName: 'phonenumber', + label: '手机号码', + defaultValue: undefined, + rules: z + .string() + .regex(/^1[3-9]\d{9}$/, '请输入正确的手机号码') + .optional() + .or(z.literal('')), + }, + // { + // component: 'Input', + // fieldName: 'email', + // defaultValue: undefined, + // label: '邮箱', + // /** + // * z.literal 是 Zod 中的一种类型,用于定义一个特定的字面量值。 + // * 它可以用于确保输入的值与指定的字面量完全匹配。 + // * 例如,你可以使用 z.literal 来确保某个字段的值只能是特定的字符串、数字、布尔值等。 + // * 即空字符串也可通过校验 + // */ + // rules: z.string().email('请输入正确的邮箱').optional().or(z.literal('')), + // }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: getDictOptions(DictEnum.SYS_USER_SEX), + optionType: 'button', + }, + defaultValue: '0', + fieldName: 'sex', + formItemClass: 'col-span-2 lg:col-span-1', + label: '性别', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: getDictOptions(DictEnum.SYS_NORMAL_DISABLE), + optionType: 'button', + }, + defaultValue: '0', + fieldName: 'status', + formItemClass: 'col-span-2 lg:col-span-1', + label: '状态', + }, + // { + // component: 'Select', + // componentProps: { + // getPopupContainer, + // mode: 'multiple', + // optionFilterProp: 'label', + // optionLabelProp: 'label', + // placeholder: '请先选择部门', + // }, + // fieldName: 'postIds', + // help: '选择部门后, 将自动加载该部门下所有的岗位', + // label: '岗位', + // }, + { + component: 'Select', + componentProps: { + getPopupContainer, + mode: 'multiple', + optionFilterProp: 'title', + optionLabelProp: 'title', + }, + fieldName: 'roleIds', + label: '角色', + }, + { + component: 'Textarea', + fieldName: 'remark', + formItemClass: 'items-baseline', + label: '备注', + }, +]; diff --git a/apps/web-antd/src/views/system/user/dept-tree.vue b/apps/web-antd/src/views/system/user/dept-tree.vue new file mode 100644 index 0000000..80c99cb --- /dev/null +++ b/apps/web-antd/src/views/system/user/dept-tree.vue @@ -0,0 +1,212 @@ + + + + + diff --git a/apps/web-antd/src/views/system/user/index.vue b/apps/web-antd/src/views/system/user/index.vue new file mode 100644 index 0000000..0827765 --- /dev/null +++ b/apps/web-antd/src/views/system/user/index.vue @@ -0,0 +1,299 @@ + + + + diff --git a/apps/web-antd/src/views/system/user/info.tsx b/apps/web-antd/src/views/system/user/info.tsx new file mode 100644 index 0000000..cd00bb8 --- /dev/null +++ b/apps/web-antd/src/views/system/user/info.tsx @@ -0,0 +1,129 @@ +import type { DescItem } from '#/components/description'; + +import { DictEnum } from '@vben/constants'; + +import { Tag } from 'ant-design-vue'; +import dayjs from 'dayjs'; +import duration from 'dayjs/plugin/duration'; +import relativeTime from 'dayjs/plugin/relativeTime'; + +import { renderDict } from '#/utils/render'; + +dayjs.extend(duration); +dayjs.extend(relativeTime); + +function renderTags(list: string[]) { + return ( +
    + {list.map((item) => ( + {item} + ))} +
    + ); +} + +export const descSchema: DescItem[] = [ + { + field: 'userId', + label: '用户ID', + }, + { + field: 'status', + label: '用户状态', + render(value) { + return renderDict(value, DictEnum.SYS_NORMAL_DISABLE); + }, + }, + { + field: 'nickName', + label: '用户信息', + render(_, data) { + const { deptName = '暂无部门信息', nickName, userName } = data; + // 为了兼容新版本和旧版本 + let currentDept = deptName; + if (data.dept && data.dept.deptName) { + currentDept = data.dept.deptName; + } + return `${userName} / ${nickName} / ${currentDept}`; + }, + }, + { + field: 'phonenumber', + label: '手机号', + render(value) { + return value || '未设置手机号码'; + }, + }, + { + field: 'email', + label: '邮箱', + render(value) { + return value || '未设置邮箱地址'; + }, + }, + { + field: 'postNames', + label: '岗位', + render(value) { + if (Array.isArray(value) && value.length === 0) { + return '暂无信息'; + } + return renderTags(value); + }, + }, + { + field: 'roleNames', + label: '权限', + render(value) { + if (Array.isArray(value) && value.length === 0) { + return '暂无信息'; + } + return renderTags(value); + }, + }, + { + field: 'createTime', + label: '创建时间', + }, + { + field: 'loginIp', + label: '上次登录IP', + render(value) { + return value || 从未登录过; + }, + }, + { + field: 'loginDate', + label: '上次登录时间', + render(value) { + if (!value) { + return 从未登录过; + } + // 默认en显示 + dayjs.locale('zh-cn'); + // 计算相差秒数 + const diffSeconds = dayjs().diff(dayjs(value), 'second'); + /** + * 转为时间显示(x月 x天) + * https://dayjs.fenxianglu.cn/category/duration.html#%E4%BA%BA%E6%80%A7%E5%8C%96 + * + */ + const diffText = dayjs.duration(diffSeconds, 'seconds').humanize(); + return ( +
    + {value} + + {diffText}前 + +
    + ); + }, + }, + { + field: 'remark', + label: '备注', + render(value) { + return value || '无'; + }, + }, +]; diff --git a/apps/web-antd/src/views/system/user/user-drawer.vue b/apps/web-antd/src/views/system/user/user-drawer.vue new file mode 100644 index 0000000..f3ae9e5 --- /dev/null +++ b/apps/web-antd/src/views/system/user/user-drawer.vue @@ -0,0 +1,234 @@ + + + diff --git a/apps/web-antd/src/views/system/user/user-import-modal.vue b/apps/web-antd/src/views/system/user/user-import-modal.vue new file mode 100644 index 0000000..fe81465 --- /dev/null +++ b/apps/web-antd/src/views/system/user/user-import-modal.vue @@ -0,0 +1,108 @@ + + + diff --git a/apps/web-antd/src/views/system/user/user-info-modal.vue b/apps/web-antd/src/views/system/user/user-info-modal.vue new file mode 100644 index 0000000..63559af --- /dev/null +++ b/apps/web-antd/src/views/system/user/user-info-modal.vue @@ -0,0 +1,62 @@ + + + diff --git a/apps/web-antd/src/views/system/user/user-modal.vue b/apps/web-antd/src/views/system/user/user-modal.vue new file mode 100644 index 0000000..b5561dc --- /dev/null +++ b/apps/web-antd/src/views/system/user/user-modal.vue @@ -0,0 +1,264 @@ + + + diff --git a/apps/web-antd/src/views/system/user/user-reset-pwd-modal.vue b/apps/web-antd/src/views/system/user/user-reset-pwd-modal.vue new file mode 100644 index 0000000..a3acf33 --- /dev/null +++ b/apps/web-antd/src/views/system/user/user-reset-pwd-modal.vue @@ -0,0 +1,111 @@ + + + diff --git a/apps/web-antd/src/views/tasks/approval-modal.vue b/apps/web-antd/src/views/tasks/approval-modal.vue new file mode 100644 index 0000000..197d92d --- /dev/null +++ b/apps/web-antd/src/views/tasks/approval-modal.vue @@ -0,0 +1,470 @@ + + + + + + diff --git a/apps/web-antd/src/views/tasks/data.tsx b/apps/web-antd/src/views/tasks/data.tsx new file mode 100644 index 0000000..179b4fa --- /dev/null +++ b/apps/web-antd/src/views/tasks/data.tsx @@ -0,0 +1,388 @@ +/* + * @Author: Mm + * @Date: 2025-05-09 10:56:48 + * @LastEditors: Mm + * @LastEditTime: 2025-06-11 11:28:31 + * 不写bug的程序员不是一个好程序员! + */ + +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +import { DictEnum } from '@vben/constants'; +import { getPopupContainer } from '@vben/utils'; + +import { getDictOptions } from '#/utils/dict'; +import { renderDict } from '#/utils/render'; +import { getDeptTree } from '#/api/system/user'; +import select from './select.vue'; +import { markRaw } from 'vue'; +// 使用 markRaw 标记组件为“原始”对象,避免被响应式追踪 +const SelectComponent = markRaw(select); +export const querySchema: FormSchemaGetter = () => [ + { + component: SelectComponent, + fieldName: 'status', + formItemClass: 'col-span-4 hide-label', + defaultValue: 1, + }, + + { + component: 'Input', + fieldName: 'title', + label: '任务标题', + }, + { + component: 'ApiTreeSelect', + componentProps: { + api: getDeptTree, + fieldNames: { label: 'label', value: 'id', children: 'children' }, + treeLine: { showLeafIcon: false }, + // 默认展开的节点 + // treeDefaultExpandedKeys: [340100000000], + showSearch: true, + treeNodeFilterProp: 'label', + }, + fieldName: 'deptId', + label: '发起单位', + }, + + { + component: 'Select', + componentProps: { + options: getDictOptions('tasks_type'), + }, + fieldName: 'taskType', + label: '任务类型', + }, + { + component: 'Select', + componentProps: { + options: getDictOptions('task_level'), + }, + fieldName: 'level', + label: '任务级别', + }, + { + component: 'RangePicker', + fieldName: 'startTimes', + label: '发起时间', + componentProps: { + valueFormat: 'YYYY-MM-DD HH:mm:ss', + }, + }, +]; + +export const columns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + { + title: '任务标题', + field: 'missionTitle', + }, + { + title: '状态', + field: 'missionStatus', + slots: { default: 'missionStatus' }, + }, + + { + title: '任务级别', + field: 'missionLevel', + }, + { + title: '任务类型', + field: 'missionTyle', + slots: { + default: ({ row }) => { + return renderDict(row.missionTyle, 'tasks_type'); + }, + }, + }, + { + title: '发起时间', + field: 'startTime', + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 200, + }, +]; +export const queryDwspSchema: FormSchemaGetter = () => [ + // { + // component: SelectComponent, + // fieldName: 'status', + // formItemClass: 'col-span-4 hide-label', + // defaultValue: 1, + // }, + + { + component: 'Input', + fieldName: 'title', + label: '任务标题', + }, + { + component: 'Input', + fieldName: 'startUserName', + label: '发起人', + }, + { + component: 'ApiTreeSelect', + componentProps: { + api: getDeptTree, + fieldNames: { label: 'label', value: 'id', children: 'children' }, + treeLine: { showLeafIcon: false }, + // 默认展开的节点 + // treeDefaultExpandedKeys: [340100000000], + showSearch: true, + treeNodeFilterProp: 'label', + }, + fieldName: 'deptId', + label: '发起单位', + }, + + { + component: 'Select', + componentProps: { + options: getDictOptions('tasks_type'), + }, + fieldName: 'taskType', + label: '任务类型', + }, + { + component: 'Select', + componentProps: { + options: getDictOptions('task_level'), + }, + fieldName: 'level', + label: '任务级别', + }, + { + component: 'RangePicker', + fieldName: 'startTimes', + label: '发起时间', + componentProps: { + valueFormat: 'YYYY-MM-DD HH:mm:ss', + }, + }, +]; +export const dwspColumns: VxeGridProps['columns'] = [ + { type: 'checkbox', width: 60 }, + { + title: '任务标题', + field: 'missionTitle', + }, + { + title: '状态', + field: 'missionStatus', + slots: { default: 'missionStatus' }, + }, + { + title: '任务级别', + field: 'missionLevel', + }, + + { + title: '发起人', + field: 'startUser', + slots: { default: 'startUser' }, + }, + { + title: '发起单位', + field: 'owerDeptName', + }, + { + title: '任务类型', + field: 'missionTyle', + slots: { + default: ({ row }) => { + return renderDict(row.missionTyle, 'tasks_type'); + }, + }, + width: 120, + }, + { + title: '发起时间', + field: 'startTime', + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 150, + }, +]; + +export const dwblColumns: VxeGridProps['columns'] = [ + // { type: 'checkbox', width: 60 }, + { + type: 'expand', + width: 40, + slots: { content: 'expand_content' }, + }, + + { + title: '任务标题', + field: 'title', + }, + { + title: '任务级别', + field: 'level', + }, + { + title: '状态', + field: 'status', + slots: { + default: ({ row }) => { + return renderDict(row.status, 'biz_task_status'); + }, + }, + }, + { + title: '发起人', + field: 'applyUserName', + }, + { + title: '发起单位', + field: 'applyDeptName', + }, + { + title: '任务类型', + field: 'type', + slots: { + default: ({ row }) => { + return renderDict(row.type, 'tasks_type'); + }, + }, + width: 120, + }, + { + title: '发起时间', + field: 'createTime', + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 220, + }, +]; +export const ycldColumns: VxeGridProps['columns'] = [ + // { type: 'checkbox', width: 60 }, + { + type: 'expand', + width: 40, + slots: { content: 'expand_content' }, + }, + { + title: '任务标题', + field: 'title', + }, + { + title: '任务级别', + field: 'level', + }, + { + title: '状态', + field: 'status', + slots: { + default: ({ row }) => { + return renderDict(row.status, 'biz_task_status'); + }, + }, + }, + { + title: '发起人', + field: 'applyUserName', + }, + { + title: '发起单位', + field: 'applyDeptName', + }, + { + title: '任务类型', + field: 'type', + slots: { + default: ({ row }) => { + return renderDict(row.type, 'tasks_type'); + }, + }, + width: 120, + }, + { + title: '发起时间', + field: 'createTime', + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 120, + }, +]; +export const modalSchema: FormSchemaGetter = () => [ + { + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + fieldName: 'postId', + label: 'postId', + }, + { + component: 'TreeSelect', + componentProps: { + getPopupContainer, + }, + fieldName: 'deptId', + label: '所属部门', + rules: 'selectRequired', + }, + { + component: 'Input', + fieldName: 'postName', + label: '岗位名称', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'postCode', + label: '岗位编码', + rules: 'required', + }, + // { + // component: 'Input', + // fieldName: 'postCategory', + // label: '类别编码', + // }, + { + component: 'InputNumber', + fieldName: 'postSort', + label: '岗位排序', + rules: 'required', + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: getDictOptions(DictEnum.SYS_NORMAL_DISABLE), + optionType: 'button', + }, + defaultValue: '0', + fieldName: 'status', + label: '岗位状态', + rules: 'required', + }, + { + component: 'Textarea', + fieldName: 'remark', + formItemClass: 'items-baseline', + label: '备注', + }, +]; diff --git a/apps/web-antd/src/views/tasks/detail.vue b/apps/web-antd/src/views/tasks/detail.vue new file mode 100644 index 0000000..d1d4e6e --- /dev/null +++ b/apps/web-antd/src/views/tasks/detail.vue @@ -0,0 +1,542 @@ + + + diff --git a/apps/web-antd/src/views/tasks/fp-dept-modal.vue b/apps/web-antd/src/views/tasks/fp-dept-modal.vue new file mode 100644 index 0000000..11f569c --- /dev/null +++ b/apps/web-antd/src/views/tasks/fp-dept-modal.vue @@ -0,0 +1,490 @@ + + + + + + diff --git a/apps/web-antd/src/views/tasks/fp-modal---第一版本.vue b/apps/web-antd/src/views/tasks/fp-modal---第一版本.vue new file mode 100644 index 0000000..383398b --- /dev/null +++ b/apps/web-antd/src/views/tasks/fp-modal---第一版本.vue @@ -0,0 +1,563 @@ + + + + + + diff --git a/apps/web-antd/src/views/tasks/fp-modal.vue b/apps/web-antd/src/views/tasks/fp-modal.vue new file mode 100644 index 0000000..a938df1 --- /dev/null +++ b/apps/web-antd/src/views/tasks/fp-modal.vue @@ -0,0 +1,446 @@ + + + + + + diff --git a/apps/web-antd/src/views/tasks/fp-user-modal.vue b/apps/web-antd/src/views/tasks/fp-user-modal.vue new file mode 100644 index 0000000..3c73f6f --- /dev/null +++ b/apps/web-antd/src/views/tasks/fp-user-modal.vue @@ -0,0 +1,432 @@ + + + + + + diff --git a/apps/web-antd/src/views/tasks/index.vue b/apps/web-antd/src/views/tasks/index.vue new file mode 100644 index 0000000..eff8556 --- /dev/null +++ b/apps/web-antd/src/views/tasks/index.vue @@ -0,0 +1,497 @@ + + + + diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/textarea/index.ts b/packages/@core/ui-kit/shadcn-ui/src/ui/textarea/index.ts new file mode 100644 index 0000000..e2d4773 --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/src/ui/textarea/index.ts @@ -0,0 +1 @@ +export { default as Textarea } from './Textarea.vue'; diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/toggle-group/ToggleGroup.vue b/packages/@core/ui-kit/shadcn-ui/src/ui/toggle-group/ToggleGroup.vue new file mode 100644 index 0000000..8d2d811 --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/src/ui/toggle-group/ToggleGroup.vue @@ -0,0 +1,42 @@ + + + diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/toggle-group/ToggleGroupItem.vue b/packages/@core/ui-kit/shadcn-ui/src/ui/toggle-group/ToggleGroupItem.vue new file mode 100644 index 0000000..7a47630 --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/src/ui/toggle-group/ToggleGroupItem.vue @@ -0,0 +1,46 @@ + + + diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/toggle-group/index.ts b/packages/@core/ui-kit/shadcn-ui/src/ui/toggle-group/index.ts new file mode 100644 index 0000000..ac3e34b --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/src/ui/toggle-group/index.ts @@ -0,0 +1,2 @@ +export { default as ToggleGroup } from './ToggleGroup.vue'; +export { default as ToggleGroupItem } from './ToggleGroupItem.vue'; diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/toggle/Toggle.vue b/packages/@core/ui-kit/shadcn-ui/src/ui/toggle/Toggle.vue new file mode 100644 index 0000000..e9bd87a --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/src/ui/toggle/Toggle.vue @@ -0,0 +1,45 @@ + + + diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/toggle/index.ts b/packages/@core/ui-kit/shadcn-ui/src/ui/toggle/index.ts new file mode 100644 index 0000000..ecc1e3f --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/src/ui/toggle/index.ts @@ -0,0 +1,2 @@ +export * from './toggle'; +export { default as Toggle } from './Toggle.vue'; diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/toggle/toggle.ts b/packages/@core/ui-kit/shadcn-ui/src/ui/toggle/toggle.ts new file mode 100644 index 0000000..d251328 --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/src/ui/toggle/toggle.ts @@ -0,0 +1,27 @@ +import type { VariantProps } from 'class-variance-authority'; + +import { cva } from 'class-variance-authority'; + +export const toggleVariants = cva( + 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground', + { + defaultVariants: { + size: 'default', + variant: 'default', + }, + variants: { + size: { + default: 'h-9 px-3', + lg: 'h-10 px-3', + sm: 'h-8 px-2', + }, + variant: { + default: 'bg-transparent', + outline: + 'border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground', + }, + }, + }, +); + +export type ToggleVariants = VariantProps; diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/Tooltip.vue b/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/Tooltip.vue new file mode 100644 index 0000000..8ab8fa6 --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/Tooltip.vue @@ -0,0 +1,16 @@ + + + diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/TooltipContent.vue b/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/TooltipContent.vue new file mode 100644 index 0000000..7537384 --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/TooltipContent.vue @@ -0,0 +1,48 @@ + + + diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/TooltipProvider.vue b/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/TooltipProvider.vue new file mode 100644 index 0000000..bf843fa --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/TooltipProvider.vue @@ -0,0 +1,13 @@ + + + diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/TooltipTrigger.vue b/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/TooltipTrigger.vue new file mode 100644 index 0000000..ead771e --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/TooltipTrigger.vue @@ -0,0 +1,13 @@ + + + diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/index.ts b/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/index.ts new file mode 100644 index 0000000..94e681c --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/index.ts @@ -0,0 +1,4 @@ +export { default as Tooltip } from './Tooltip.vue'; +export { default as TooltipContent } from './TooltipContent.vue'; +export { default as TooltipProvider } from './TooltipProvider.vue'; +export { default as TooltipTrigger } from './TooltipTrigger.vue'; diff --git a/packages/@core/ui-kit/shadcn-ui/tailwind.config.mjs b/packages/@core/ui-kit/shadcn-ui/tailwind.config.mjs new file mode 100644 index 0000000..f17f556 --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/tailwind.config.mjs @@ -0,0 +1 @@ +export { default } from '@vben/tailwind-config'; diff --git a/packages/@core/ui-kit/shadcn-ui/tsconfig.json b/packages/@core/ui-kit/shadcn-ui/tsconfig.json new file mode 100644 index 0000000..0a46bc5 --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/tsconfig.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@vben-core/shadcn-ui/*": ["./src/*"] + } + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/@core/ui-kit/tabs-ui/build.config.ts b/packages/@core/ui-kit/tabs-ui/build.config.ts new file mode 100644 index 0000000..18eaa60 --- /dev/null +++ b/packages/@core/ui-kit/tabs-ui/build.config.ts @@ -0,0 +1,21 @@ +import { defineBuildConfig } from 'unbuild'; + +export default defineBuildConfig({ + clean: true, + declaration: true, + entries: [ + { + builder: 'mkdist', + input: './src', + loaders: ['vue'], + pattern: ['**/*.vue'], + }, + { + builder: 'mkdist', + format: 'esm', + input: './src', + loaders: ['js'], + pattern: ['**/*.ts'], + }, + ], +}); diff --git a/packages/@core/ui-kit/tabs-ui/package.json b/packages/@core/ui-kit/tabs-ui/package.json new file mode 100644 index 0000000..af32067 --- /dev/null +++ b/packages/@core/ui-kit/tabs-ui/package.json @@ -0,0 +1,47 @@ +{ + "name": "@vben-core/tabs-ui", + "version": "5.5.3", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/@vben-core/uikit/tabs-ui" + }, + "license": "MIT", + "type": "module", + "scripts": { + "build": "pnpm unbuild", + "prepublishOnly": "npm run build" + }, + "files": [ + "dist" + ], + "sideEffects": [ + "**/*.css" + ], + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "exports": { + ".": { + "types": "./src/index.ts", + "development": "./src/index.ts", + "default": "./dist/index.mjs" + } + }, + "publishConfig": { + "exports": { + ".": { + "default": "./dist/index.mjs" + } + } + }, + "dependencies": { + "@vben-core/composables": "workspace:*", + "@vben-core/icons": "workspace:*", + "@vben-core/shadcn-ui": "workspace:*", + "@vben-core/typings": "workspace:*", + "@vueuse/core": "catalog:", + "vue": "catalog:" + } +} diff --git a/packages/@core/ui-kit/tabs-ui/postcss.config.mjs b/packages/@core/ui-kit/tabs-ui/postcss.config.mjs new file mode 100644 index 0000000..3d80704 --- /dev/null +++ b/packages/@core/ui-kit/tabs-ui/postcss.config.mjs @@ -0,0 +1 @@ +export { default } from '@vben/tailwind-config/postcss'; diff --git a/packages/@core/ui-kit/tabs-ui/src/components/index.ts b/packages/@core/ui-kit/tabs-ui/src/components/index.ts new file mode 100644 index 0000000..3c28c94 --- /dev/null +++ b/packages/@core/ui-kit/tabs-ui/src/components/index.ts @@ -0,0 +1,2 @@ +export { default as Tabs } from './tabs/tabs.vue'; +export { default as TabsChrome } from './tabs-chrome/tabs.vue'; diff --git a/packages/@core/ui-kit/tabs-ui/src/components/tabs-chrome/tabs.vue b/packages/@core/ui-kit/tabs-ui/src/components/tabs-chrome/tabs.vue new file mode 100644 index 0000000..4d81bee --- /dev/null +++ b/packages/@core/ui-kit/tabs-ui/src/components/tabs-chrome/tabs.vue @@ -0,0 +1,209 @@ + + + + + diff --git a/packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue b/packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue new file mode 100644 index 0000000..6197ff9 --- /dev/null +++ b/packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue @@ -0,0 +1,148 @@ + + + diff --git a/packages/@core/ui-kit/tabs-ui/src/components/widgets/index.ts b/packages/@core/ui-kit/tabs-ui/src/components/widgets/index.ts new file mode 100644 index 0000000..a26899e --- /dev/null +++ b/packages/@core/ui-kit/tabs-ui/src/components/widgets/index.ts @@ -0,0 +1,2 @@ +export { default as TabsToolMore } from './tool-more.vue'; +export { default as TabsToolScreen } from './tool-screen.vue'; diff --git a/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-more.vue b/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-more.vue new file mode 100644 index 0000000..0cbfc73 --- /dev/null +++ b/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-more.vue @@ -0,0 +1,18 @@ + + + diff --git a/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-screen.vue b/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-screen.vue new file mode 100644 index 0000000..0183719 --- /dev/null +++ b/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-screen.vue @@ -0,0 +1,19 @@ + + + diff --git a/packages/@core/ui-kit/tabs-ui/src/index.ts b/packages/@core/ui-kit/tabs-ui/src/index.ts new file mode 100644 index 0000000..6d562db --- /dev/null +++ b/packages/@core/ui-kit/tabs-ui/src/index.ts @@ -0,0 +1,3 @@ +export * from './components/widgets'; +export { default as TabsView } from './tabs-view.vue'; +export type { IContextMenuItem } from '@vben-core/shadcn-ui'; diff --git a/packages/@core/ui-kit/tabs-ui/src/tabs-view.vue b/packages/@core/ui-kit/tabs-ui/src/tabs-view.vue new file mode 100644 index 0000000..daf9f77 --- /dev/null +++ b/packages/@core/ui-kit/tabs-ui/src/tabs-view.vue @@ -0,0 +1,106 @@ + + + diff --git a/packages/@core/ui-kit/tabs-ui/src/types.ts b/packages/@core/ui-kit/tabs-ui/src/types.ts new file mode 100644 index 0000000..19ecf03 --- /dev/null +++ b/packages/@core/ui-kit/tabs-ui/src/types.ts @@ -0,0 +1,73 @@ +import type { IContextMenuItem } from '@vben-core/shadcn-ui'; +import type { TabDefinition, TabsStyleType } from '@vben-core/typings'; + +export type TabsEmits = { + close: [string]; + sortTabs: [number, number]; + unpin: [TabDefinition]; +}; + +export interface TabsProps { + active?: string; + /** + * @zh_CN content class + * @default tabs-chrome + */ + contentClass?: string; + /** + * @zh_CN 右键菜单 + */ + contextMenus?: (data: any) => IContextMenuItem[]; + /** + * @zh_CN 是否可以拖拽 + */ + draggable?: boolean; + /** + * @zh_CN 间隙 + * @default 7 + * 仅限 tabs-chrome + */ + gap?: number; + /** + * @zh_CN tab 最大宽度 + * 仅限 tabs-chrome + */ + maxWidth?: number; + /** + * @zh_CN 点击中键时关闭Tab + */ + middleClickToClose?: boolean; + + /** + * @zh_CN tab最小宽度 + * 仅限 tabs-chrome + */ + minWidth?: number; + + /** + * @zh_CN 是否显示图标 + */ + showIcon?: boolean; + /** + * @zh_CN 标签页风格 + */ + styleType?: TabsStyleType; + + /** + * @zh_CN 选项卡数据 + */ + tabs?: TabDefinition[]; + + /** + * @zh_CN 是否响应滚轮事件 + */ + wheelable?: boolean; +} + +export interface TabConfig extends TabDefinition { + affixTab: boolean; + closable: boolean; + icon: string; + key: string; + title: string; +} diff --git a/packages/@core/ui-kit/tabs-ui/src/use-tabs-drag.ts b/packages/@core/ui-kit/tabs-ui/src/use-tabs-drag.ts new file mode 100644 index 0000000..4693509 --- /dev/null +++ b/packages/@core/ui-kit/tabs-ui/src/use-tabs-drag.ts @@ -0,0 +1,123 @@ +import type { Sortable } from '@vben-core/composables'; +import type { EmitType } from '@vben-core/typings'; + +import type { TabsProps } from './types'; + +import { useIsMobile, useSortable } from '@vben-core/composables'; +import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue'; + +// 可能会找到拖拽的子元素,这里需要确保拖拽的dom时tab元素 +function findParentElement(element: HTMLElement) { + const parentCls = 'group'; + return element.classList.contains(parentCls) + ? element + : element.closest(`.${parentCls}`); +} + +export function useTabsDrag(props: TabsProps, emit: EmitType) { + const sortableInstance = ref(null); + + async function initTabsSortable() { + await nextTick(); + + const el = document.querySelectorAll( + `.${props.contentClass}`, + )?.[0] as HTMLElement; + + if (!el) { + console.warn('Element not found for sortable initialization'); + return; + } + + const resetElState = async () => { + el.style.cursor = 'default'; + // el.classList.remove('dragging'); + el.querySelector('.draggable')?.classList.remove('dragging'); + }; + + const { initializeSortable } = useSortable(el, { + filter: (_evt, target: HTMLElement) => { + const parent = findParentElement(target); + const draggable = parent?.classList.contains('draggable'); + return !draggable || !props.draggable; + }, + onEnd(evt) { + const { newIndex, oldIndex } = evt; + // const fromElement = evt.item; + const { srcElement } = (evt as any).originalEvent; + + if (!srcElement) { + resetElState(); + return; + } + + const srcParent = findParentElement(srcElement); + + if (!srcParent) { + resetElState(); + return; + } + + if (!srcParent.classList.contains('draggable')) { + resetElState(); + + return; + } + + if ( + oldIndex !== undefined && + newIndex !== undefined && + !Number.isNaN(oldIndex) && + !Number.isNaN(newIndex) && + oldIndex !== newIndex + ) { + emit('sortTabs', oldIndex, newIndex); + } + resetElState(); + }, + onMove(evt) { + const parent = findParentElement(evt.related); + if (parent?.classList.contains('draggable') && props.draggable) { + const isCurrentAffix = evt.dragged.classList.contains('affix-tab'); + const isRelatedAffix = evt.related.classList.contains('affix-tab'); + // 不允许在固定的tab和非固定的tab之间互相拖拽 + return isCurrentAffix === isRelatedAffix; + } else { + return false; + } + }, + onStart: () => { + el.style.cursor = 'grabbing'; + el.querySelector('.draggable')?.classList.add('dragging'); + // el.classList.add('dragging'); + }, + }); + + sortableInstance.value = await initializeSortable(); + } + + async function init() { + const { isMobile } = useIsMobile(); + + // 移动端下tab不需要拖拽 + if (isMobile.value) { + return; + } + await nextTick(); + initTabsSortable(); + } + + onMounted(init); + + watch( + () => props.styleType, + () => { + sortableInstance.value?.destroy(); + init(); + }, + ); + + onUnmounted(() => { + sortableInstance.value?.destroy(); + }); +} diff --git a/packages/@core/ui-kit/tabs-ui/src/use-tabs-view-scroll.ts b/packages/@core/ui-kit/tabs-ui/src/use-tabs-view-scroll.ts new file mode 100644 index 0000000..dc2f70e --- /dev/null +++ b/packages/@core/ui-kit/tabs-ui/src/use-tabs-view-scroll.ts @@ -0,0 +1,202 @@ +import type { TabsProps } from './types'; + +import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue'; + +import { VbenScrollbar } from '@vben-core/shadcn-ui'; + +import { useDebounceFn } from '@vueuse/core'; + +type DomElement = Element | null | undefined; + +export function useTabsViewScroll(props: TabsProps) { + let resizeObserver: null | ResizeObserver = null; + let mutationObserver: MutationObserver | null = null; + let tabItemCount = 0; + const scrollbarRef = ref | null>(null); + const scrollViewportEl = ref(null); + const showScrollButton = ref(false); + const scrollIsAtLeft = ref(true); + const scrollIsAtRight = ref(false); + + function getScrollClientWidth() { + const scrollbarEl = scrollbarRef.value?.$el; + if (!scrollbarEl || !scrollViewportEl.value) return {}; + + const scrollbarWidth = scrollbarEl.clientWidth; + const scrollViewWidth = scrollViewportEl.value.clientWidth; + + return { + scrollbarWidth, + scrollViewWidth, + }; + } + + function scrollDirection( + direction: 'left' | 'right', + distance: number = 150, + ) { + const { scrollbarWidth, scrollViewWidth } = getScrollClientWidth(); + + if (!scrollbarWidth || !scrollViewWidth) return; + + if (scrollbarWidth > scrollViewWidth) return; + + scrollViewportEl.value?.scrollBy({ + behavior: 'smooth', + left: + direction === 'left' + ? -(scrollbarWidth - distance) + : +(scrollbarWidth - distance), + }); + } + + async function initScrollbar() { + await nextTick(); + + const scrollbarEl = scrollbarRef.value?.$el; + if (!scrollbarEl) { + return; + } + + const viewportEl = scrollbarEl?.querySelector( + 'div[data-radix-scroll-area-viewport]', + ); + + scrollViewportEl.value = viewportEl; + calcShowScrollbarButton(); + + await nextTick(); + scrollToActiveIntoView(); + + // 监听大小变化 + resizeObserver?.disconnect(); + resizeObserver = new ResizeObserver( + useDebounceFn((_entries: ResizeObserverEntry[]) => { + calcShowScrollbarButton(); + scrollToActiveIntoView(); + }, 100), + ); + resizeObserver.observe(viewportEl); + + tabItemCount = props.tabs?.length || 0; + mutationObserver?.disconnect(); + // 使用 MutationObserver 仅监听子节点数量变化 + mutationObserver = new MutationObserver(() => { + const count = viewportEl.querySelectorAll( + `div[data-tab-item="true"]`, + ).length; + + if (count > tabItemCount) { + scrollToActiveIntoView(); + } + + if (count !== tabItemCount) { + calcShowScrollbarButton(); + tabItemCount = count; + } + }); + + // 配置为仅监听子节点的添加和移除 + mutationObserver.observe(viewportEl, { + attributes: false, + childList: true, + subtree: true, + }); + } + + async function scrollToActiveIntoView() { + if (!scrollViewportEl.value) { + return; + } + await nextTick(); + const viewportEl = scrollViewportEl.value; + const { scrollbarWidth } = getScrollClientWidth(); + const { scrollWidth } = viewportEl; + + if (scrollbarWidth >= scrollWidth) { + return; + } + + requestAnimationFrame(() => { + const activeItem = viewportEl?.querySelector('.is-active'); + activeItem?.scrollIntoView({ behavior: 'smooth', inline: 'start' }); + }); + } + + /** + * 计算tabs 宽度,用于判断是否显示左右滚动按钮 + */ + async function calcShowScrollbarButton() { + if (!scrollViewportEl.value) { + return; + } + + const { scrollbarWidth } = getScrollClientWidth(); + + showScrollButton.value = + scrollViewportEl.value.scrollWidth > scrollbarWidth; + } + + const handleScrollAt = useDebounceFn(({ left, right }) => { + scrollIsAtLeft.value = left; + scrollIsAtRight.value = right; + }, 100); + + function handleWheel({ deltaY }: WheelEvent) { + scrollViewportEl.value?.scrollBy({ + // behavior: 'smooth', + left: deltaY * 3, + }); + } + + watch( + () => props.active, + async () => { + // 200为了等待 tab 切换动画完成 + // setTimeout(() => { + scrollToActiveIntoView(); + // }, 300); + }, + { + flush: 'post', + }, + ); + + // watch( + // () => props.tabs?.length, + // async () => { + // await nextTick(); + // calcShowScrollbarButton(); + // }, + // { + // flush: 'post', + // }, + // ); + + watch( + () => props.styleType, + () => { + initScrollbar(); + }, + ); + + onMounted(initScrollbar); + + onUnmounted(() => { + resizeObserver?.disconnect(); + mutationObserver?.disconnect(); + resizeObserver = null; + mutationObserver = null; + }); + + return { + handleScrollAt, + handleWheel, + initScrollbar, + scrollbarRef, + scrollDirection, + scrollIsAtLeft, + scrollIsAtRight, + showScrollButton, + }; +} diff --git a/packages/@core/ui-kit/tabs-ui/tailwind.config.mjs b/packages/@core/ui-kit/tabs-ui/tailwind.config.mjs new file mode 100644 index 0000000..f17f556 --- /dev/null +++ b/packages/@core/ui-kit/tabs-ui/tailwind.config.mjs @@ -0,0 +1 @@ +export { default } from '@vben/tailwind-config'; diff --git a/packages/@core/ui-kit/tabs-ui/tsconfig.json b/packages/@core/ui-kit/tabs-ui/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/packages/@core/ui-kit/tabs-ui/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/constants/README.md b/packages/constants/README.md new file mode 100644 index 0000000..1fae490 --- /dev/null +++ b/packages/constants/README.md @@ -0,0 +1,19 @@ +# @vben/constants + +用于多个 `app` 公用的常量,继承了 `@vben-core/shared/constants` 的所有能力。业务上有通用常量可以放在这里。 + +## 用法 + +### 添加依赖 + +```bash +# 进入目标应用目录,例如 apps/xxxx-app +# cd apps/xxxx-app +pnpm add @vben/constants +``` + +### 使用 + +```ts +import { DEFAULT_HOME_PATH } from '@vben/constants'; +``` diff --git a/packages/constants/package.json b/packages/constants/package.json new file mode 100644 index 0000000..dae564a --- /dev/null +++ b/packages/constants/package.json @@ -0,0 +1,25 @@ +{ + "name": "@vben/constants", + "version": "5.5.3", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/constants" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben-core/shared": "workspace:*" + } +} diff --git a/packages/constants/src/core.ts b/packages/constants/src/core.ts new file mode 100644 index 0000000..24db39d --- /dev/null +++ b/packages/constants/src/core.ts @@ -0,0 +1,28 @@ +/** + * @zh_CN 登录页面 url 地址 + */ +export const LOGIN_PATH = '/auth/login'; + +/** + * @zh_CN 默认首页地址 + */ +export const DEFAULT_HOME_PATH = '/analytics'; + +export interface LanguageOption { + label: string; + value: 'en-US' | 'zh-CN'; +} + +/** + * Supported languages + */ +export const SUPPORT_LANGUAGES: LanguageOption[] = [ + { + label: '简体中文', + value: 'zh-CN', + }, + { + label: 'English', + value: 'en-US', + }, +]; diff --git a/packages/constants/src/index.ts b/packages/constants/src/index.ts new file mode 100644 index 0000000..6502c6d --- /dev/null +++ b/packages/constants/src/index.ts @@ -0,0 +1,2 @@ +export * from './core'; +export * from '@vben-core/shared/constants'; diff --git a/packages/constants/tsconfig.json b/packages/constants/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/packages/constants/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/effects/README.md b/packages/effects/README.md new file mode 100644 index 0000000..4965b51 --- /dev/null +++ b/packages/effects/README.md @@ -0,0 +1,10 @@ +## Effects 目录 + +`effects` 目录专门用于存放与轻微耦合相关的代码和逻辑。如果你的包具有以下特点,建议将其放置在 `effects` 目录下: + +- **状态管理**:使用状态管理框架 `pinia`,并包含处理副作用(如异步操作、API 调用)的部分。 +- **用户偏好设置**:使用 `@vben-core/preferences` 处理用户偏好设置,涉及本地存储或浏览器缓存逻辑(如使用 `localStorage`)。 +- **导航和路由**:处理导航、页面跳转等场景,需要管理路由变化的逻辑。 +- **组件库依赖**:包含与特定组件库紧密耦合或依赖大型仓库的部分。 + +通过将相关代码归类到 `effects` 目录,可以使项目结构更加清晰,便于维护和扩展。 diff --git a/packages/effects/access/package.json b/packages/effects/access/package.json new file mode 100644 index 0000000..7a24ba8 --- /dev/null +++ b/packages/effects/access/package.json @@ -0,0 +1,29 @@ +{ + "name": "@vben/access", + "version": "5.5.3", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/effects/permissions" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben/preferences": "workspace:*", + "@vben/stores": "workspace:*", + "@vben/types": "workspace:*", + "@vben/utils": "workspace:*", + "vue": "catalog:" + } +} diff --git a/packages/effects/access/src/access-control.vue b/packages/effects/access/src/access-control.vue new file mode 100644 index 0000000..219608e --- /dev/null +++ b/packages/effects/access/src/access-control.vue @@ -0,0 +1,47 @@ + + + + diff --git a/packages/effects/access/src/accessible.ts b/packages/effects/access/src/accessible.ts new file mode 100644 index 0000000..8a7e4e5 --- /dev/null +++ b/packages/effects/access/src/accessible.ts @@ -0,0 +1,119 @@ +import type { + AccessModeType, + GenerateMenuAndRoutesOptions, + RouteRecordRaw, +} from '@vben/types'; + +import { + cloneDeep, + generateMenus, + generateRoutesByBackend, + generateRoutesByFrontend, + mapTree, + setObjToUrlParams, +} from '@vben/utils'; + +async function generateAccessible( + mode: AccessModeType, + options: GenerateMenuAndRoutesOptions, +) { + const { router } = options; + + options.routes = cloneDeep(options.routes); + // 生成路由 + const accessibleRoutes = await generateRoutes(mode, options); + + const root = router.getRoutes().find((item) => item.path === '/'); + + // 动态添加到router实例内 + accessibleRoutes.forEach((route) => { + /** + * 外链不应该被添加到路由 由menu处理 + */ + if (/^https?:\/\//.test(route.path)) { + return; + } + if (root && !route.meta?.noBasicLayout) { + // 为了兼容之前的版本用法,如果包含子路由,则将component移除,以免出现多层BasicLayout + // 如果你的项目已经跟进了本次修改,移除了所有自定义菜单首级的BasicLayout,可以将这段if代码删除 + // TODO: 这里后期需要follow更新 + if (route.children && route.children.length > 0) { + delete route.component; + } + root.children?.push(route); + } else { + router.addRoute(route); + } + }); + + if (root) { + if (root.name) { + router.removeRoute(root.name); + } + router.addRoute(root); + } + + // 生成菜单 + const accessibleMenus = await generateMenus(accessibleRoutes, options.router); + + return { accessibleMenus, accessibleRoutes }; +} + +/** + * Generate routes + * @param mode + * @param options + */ +async function generateRoutes( + mode: AccessModeType, + options: GenerateMenuAndRoutesOptions, +) { + const { forbiddenComponent, roles, routes } = options; + + let resultRoutes: RouteRecordRaw[] = routes; + switch (mode) { + case 'backend': { + resultRoutes = await generateRoutesByBackend(options); + break; + } + case 'frontend': { + resultRoutes = await generateRoutesByFrontend( + routes, + roles || [], + forbiddenComponent, + ); + break; + } + } + + /** + * 调整路由树,做以下处理: + * 1. 对未添加redirect的路由添加redirect + */ + resultRoutes = mapTree(resultRoutes, (route) => { + // 如果有redirect或者没有子路由,则直接返回 + if (route.redirect || !route.children || route.children.length === 0) { + return route; + } + const firstChild = route.children[0]; + + // 如果子路由不是以/开头,则直接返回,这种情况需要计算全部父级的path才能得出正确的path,这里不做处理 + if (!firstChild?.path || !firstChild.path.startsWith('/')) { + return route; + } + + // 第一个路由如果有query参数 需要加上参数 + const fistChildQuery = route.children[0]?.meta?.query; + // 根目录菜单固定只有一个children 且path为/ 不需要添加redirect + route.redirect = + fistChildQuery && route.children.length !== 1 && route.path !== '/' + ? setObjToUrlParams(firstChild.path, fistChildQuery) + : firstChild.path; + + return route; + }); + + return resultRoutes; +} + +export { generateAccessible }; diff --git a/packages/effects/access/src/directive.ts b/packages/effects/access/src/directive.ts new file mode 100644 index 0000000..35d9d51 --- /dev/null +++ b/packages/effects/access/src/directive.ts @@ -0,0 +1,42 @@ +/** + * Global authority directive + * Used for fine-grained control of component permissions + * @Example v-access:role="[ROLE_NAME]" or v-access:role="ROLE_NAME" + * @Example v-access:code="[ROLE_CODE]" or v-access:code="ROLE_CODE" + */ +import type { App, Directive, DirectiveBinding } from 'vue'; + +import { useAccess } from './use-access'; + +function isAccessible( + el: Element, + binding: DirectiveBinding, +) { + const { accessMode, hasAccessByCodes, hasAccessByRoles } = useAccess(); + + const value = binding.value; + + if (!value) return; + const authMethod = + accessMode.value === 'frontend' && binding.arg === 'role' + ? hasAccessByRoles + : hasAccessByCodes; + + const values = Array.isArray(value) ? value : [value]; + + if (!authMethod(values)) { + el?.remove(); + } +} + +const mounted = (el: Element, binding: DirectiveBinding) => { + isAccessible(el, binding); +}; + +const authDirective: Directive = { + mounted, +}; + +export function registerAccessDirective(app: App) { + app.directive('access', authDirective); +} diff --git a/packages/effects/access/src/index.ts b/packages/effects/access/src/index.ts new file mode 100644 index 0000000..392aa53 --- /dev/null +++ b/packages/effects/access/src/index.ts @@ -0,0 +1,4 @@ +export { default as AccessControl } from './access-control.vue'; +export * from './accessible'; +export * from './directive'; +export * from './use-access'; diff --git a/packages/effects/access/src/use-access.ts b/packages/effects/access/src/use-access.ts new file mode 100644 index 0000000..05ac348 --- /dev/null +++ b/packages/effects/access/src/use-access.ts @@ -0,0 +1,59 @@ +import { computed } from 'vue'; + +import { preferences, updatePreferences } from '@vben/preferences'; +import { useAccessStore, useUserStore } from '@vben/stores'; + +function useAccess() { + const accessStore = useAccessStore(); + const userStore = useUserStore(); + const accessMode = computed(() => { + return preferences.app.accessMode; + }); + + /** + * 基于角色判断是否有权限 + * @description: Determine whether there is permission,The role is judged by the user's role + * @param roles + */ + function hasAccessByRoles(roles: string[]) { + const userRoleSet = new Set(userStore.userRoles); + const intersection = roles.filter((item) => userRoleSet.has(item)); + return intersection.length > 0; + } + + /** + * 基于权限码判断是否有权限 + * @description: Determine whether there is permission,The permission code is judged by the user's permission code + * @param codes + */ + function hasAccessByCodes(codes: string[]) { + const userCodesSet = new Set(accessStore.accessCodes); + /** + * 管理员权限 + */ + if (userCodesSet.has('*:*:*')) { + return true; + } + // 其他 判断是否存在 + const intersection = codes.filter((item) => userCodesSet.has(item)); + return intersection.length > 0; + } + + async function toggleAccessMode() { + updatePreferences({ + app: { + accessMode: + preferences.app.accessMode === 'frontend' ? 'backend' : 'frontend', + }, + }); + } + + return { + accessMode, + hasAccessByCodes, + hasAccessByRoles, + toggleAccessMode, + }; +} + +export { useAccess }; diff --git a/packages/effects/access/tsconfig.json b/packages/effects/access/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/packages/effects/access/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/effects/common-ui/package.json b/packages/effects/common-ui/package.json new file mode 100644 index 0000000..408424d --- /dev/null +++ b/packages/effects/common-ui/package.json @@ -0,0 +1,56 @@ +{ + "name": "@vben/common-ui", + "version": "5.5.3", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/effects/common-ui" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@codemirror/lang-html": "^6.4.9", + "@codemirror/lang-java": "^6.0.1", + "@codemirror/lang-javascript": "^6.2.2", + "@codemirror/lang-sql": "^6.7.1", + "@codemirror/lang-vue": "^0.1.3", + "@codemirror/lang-xml": "^6.1.0", + "@codemirror/theme-one-dark": "^6.1.2", + "@vben-core/form-ui": "workspace:*", + "@vben-core/popup-ui": "workspace:*", + "@vben-core/preferences": "workspace:*", + "@vben-core/shadcn-ui": "workspace:*", + "@vben-core/shared": "workspace:*", + "@vben/constants": "workspace:*", + "@vben/hooks": "workspace:*", + "@vben/icons": "workspace:*", + "@vben/locales": "workspace:*", + "@vben/preferences": "workspace:*", + "@vben/types": "workspace:*", + "@vueuse/core": "catalog:", + "@vueuse/integrations": "catalog:", + "codemirror": "6.0.1", + "qrcode": "catalog:", + "tippy.js": "catalog:", + "vditor": "3.10.7", + "vue": "catalog:", + "vue-codemirror6": "1.3.4", + "vue-json-pretty": "^2.4.0", + "vue-router": "catalog:", + "vue-tippy": "catalog:" + }, + "devDependencies": { + "@types/qrcode": "catalog:" + } +} diff --git a/packages/effects/common-ui/src/components/api-component/api-component.vue b/packages/effects/common-ui/src/components/api-component/api-component.vue new file mode 100644 index 0000000..c871fe2 --- /dev/null +++ b/packages/effects/common-ui/src/components/api-component/api-component.vue @@ -0,0 +1,206 @@ + + diff --git a/packages/effects/common-ui/src/components/api-component/index.ts b/packages/effects/common-ui/src/components/api-component/index.ts new file mode 100644 index 0000000..9815763 --- /dev/null +++ b/packages/effects/common-ui/src/components/api-component/index.ts @@ -0,0 +1 @@ +export { default as ApiComponent } from './api-component.vue'; diff --git a/packages/effects/common-ui/src/components/captcha/hooks/useCaptchaPoints.ts b/packages/effects/common-ui/src/components/captcha/hooks/useCaptchaPoints.ts new file mode 100644 index 0000000..511fb3b --- /dev/null +++ b/packages/effects/common-ui/src/components/captcha/hooks/useCaptchaPoints.ts @@ -0,0 +1,19 @@ +import type { CaptchaPoint } from '../types'; + +import { reactive } from 'vue'; + +export function useCaptchaPoints() { + const points = reactive([]); + function addPoint(point: CaptchaPoint) { + points.push(point); + } + + function clearPoints() { + points.splice(0, points.length); + } + return { + addPoint, + clearPoints, + points, + }; +} diff --git a/packages/effects/common-ui/src/components/captcha/index.ts b/packages/effects/common-ui/src/components/captcha/index.ts new file mode 100644 index 0000000..6ad68c4 --- /dev/null +++ b/packages/effects/common-ui/src/components/captcha/index.ts @@ -0,0 +1,6 @@ +export { default as PointSelectionCaptcha } from './point-selection-captcha/index.vue'; +export { default as PointSelectionCaptchaCard } from './point-selection-captcha/index.vue'; + +export { default as SliderCaptcha } from './slider-captcha/index.vue'; +export { default as SliderRotateCaptcha } from './slider-rotate-captcha/index.vue'; +export type * from './types'; diff --git a/packages/effects/common-ui/src/components/captcha/point-selection-captcha/index.vue b/packages/effects/common-ui/src/components/captcha/point-selection-captcha/index.vue new file mode 100644 index 0000000..d4b1361 --- /dev/null +++ b/packages/effects/common-ui/src/components/captcha/point-selection-captcha/index.vue @@ -0,0 +1,175 @@ + + diff --git a/packages/effects/common-ui/src/components/captcha/point-selection-captcha/point-selection-captcha-card.vue b/packages/effects/common-ui/src/components/captcha/point-selection-captcha/point-selection-captcha-card.vue new file mode 100644 index 0000000..8dd8881 --- /dev/null +++ b/packages/effects/common-ui/src/components/captcha/point-selection-captcha/point-selection-captcha-card.vue @@ -0,0 +1,82 @@ + + diff --git a/packages/effects/common-ui/src/components/captcha/slider-captcha/index.vue b/packages/effects/common-ui/src/components/captcha/slider-captcha/index.vue new file mode 100644 index 0000000..1120aab --- /dev/null +++ b/packages/effects/common-ui/src/components/captcha/slider-captcha/index.vue @@ -0,0 +1,241 @@ + + + diff --git a/packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-action.vue b/packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-action.vue new file mode 100644 index 0000000..9a9756b --- /dev/null +++ b/packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-action.vue @@ -0,0 +1,63 @@ + + + diff --git a/packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-bar.vue b/packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-bar.vue new file mode 100644 index 0000000..0884ccb --- /dev/null +++ b/packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-bar.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-content.vue b/packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-content.vue new file mode 100644 index 0000000..f545895 --- /dev/null +++ b/packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-content.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/packages/effects/common-ui/src/components/captcha/slider-rotate-captcha/index.vue b/packages/effects/common-ui/src/components/captcha/slider-rotate-captcha/index.vue new file mode 100644 index 0000000..8894589 --- /dev/null +++ b/packages/effects/common-ui/src/components/captcha/slider-rotate-captcha/index.vue @@ -0,0 +1,213 @@ + + + diff --git a/packages/effects/common-ui/src/components/captcha/types.ts b/packages/effects/common-ui/src/components/captcha/types.ts new file mode 100644 index 0000000..ddc190d --- /dev/null +++ b/packages/effects/common-ui/src/components/captcha/types.ts @@ -0,0 +1,174 @@ +import type { ClassType } from '@vben/types'; +import type { CSSProperties } from 'vue'; + +export interface CaptchaData { + /** + * x + */ + x: number; + /** + * y + */ + y: number; + /** + * 时间戳 + */ + t: number; +} +export interface CaptchaPoint extends CaptchaData { + /** + * 数据索引 + */ + i: number; +} +export interface PointSelectionCaptchaCardProps { + /** + * 验证码图片 + */ + captchaImage: string; + /** + * 验证码图片高度 + * @default '220px' + */ + height?: number | string; + /** + * 水平内边距 + * @default '12px' + */ + paddingX?: number | string; + /** + * 垂直内边距 + * @default '16px' + */ + paddingY?: number | string; + /** + * 标题 + * @default '请按图依次点击' + */ + title?: string; + /** + * 验证码图片宽度 + * @default '300px' + */ + width?: number | string; +} + +export interface PointSelectionCaptchaProps + extends PointSelectionCaptchaCardProps { + /** + * 是否展示确定按钮 + * @default false + */ + showConfirm?: boolean; + /** + * 提示图片 + * @default '' + */ + hintImage?: string; + /** + * 提示文本 + * @default '' + */ + hintText?: string; +} + +export interface SliderCaptchaProps { + class?: ClassType; + /** + * @description 滑块的样式 + * @default {} + */ + actionStyle?: CSSProperties; + + /** + * @description 滑块条的样式 + * @default {} + */ + barStyle?: CSSProperties; + + /** + * @description 内容的样式 + * @default {} + */ + contentStyle?: CSSProperties; + + /** + * @description 组件的样式 + * @default {} + */ + wrapperStyle?: CSSProperties; + + /** + * @description 是否作为插槽使用,用于联动组件,可参考旋转校验组件 + * @default false + */ + isSlot?: boolean; + + /** + * @description 验证成功的提示 + * @default '验证通过' + */ + successText?: string; + + /** + * @description 提示文字 + * @default '请按住滑块拖动' + */ + text?: string; +} + +export interface SliderRotateCaptchaProps { + /** + * @description 旋转的角度 + * @default 20 + */ + diffDegree?: number; + + /** + * @description 图片的宽度 + * @default 260 + */ + imageSize?: number; + + /** + * @description 图片的样式 + * @default {} + */ + imageWrapperStyle?: CSSProperties; + + /** + * @description 最大旋转角度 + * @default 270 + */ + maxDegree?: number; + + /** + * @description 最小旋转角度 + * @default 90 + */ + minDegree?: number; + + /** + * @description 图片的地址 + */ + src?: string; + /** + * @description 默认提示文本 + */ + defaultTip?: string; +} + +export interface CaptchaVerifyPassingData { + isPassing: boolean; + time: number | string; +} + +export interface SliderCaptchaActionType { + resume: () => void; +} + +export interface SliderRotateVerifyPassingData { + event: MouseEvent | TouchEvent; + moveDistance: number; + moveX: number; +} diff --git a/packages/effects/common-ui/src/components/code-mirror/code-mirror.vue b/packages/effects/common-ui/src/components/code-mirror/code-mirror.vue new file mode 100644 index 0000000..a449f08 --- /dev/null +++ b/packages/effects/common-ui/src/components/code-mirror/code-mirror.vue @@ -0,0 +1,68 @@ + + + diff --git a/packages/effects/common-ui/src/components/code-mirror/data.ts b/packages/effects/common-ui/src/components/code-mirror/data.ts new file mode 100644 index 0000000..3824bee --- /dev/null +++ b/packages/effects/common-ui/src/components/code-mirror/data.ts @@ -0,0 +1,24 @@ +import { html } from '@codemirror/lang-html'; +import { java } from '@codemirror/lang-java'; +import { javascript } from '@codemirror/lang-javascript'; +import { sql } from '@codemirror/lang-sql'; +import { vue } from '@codemirror/lang-vue'; +import { xml } from '@codemirror/lang-xml'; + +/** + * 可自行安装依赖并按格式配置 函数形参为配置项 + * @see https://github.com/logue/vue-codemirror6?tab=readme-ov-file#supported-languages Language Support项 + */ +export const languageSupportMap = { + html: html(), + java: java(), + js: javascript(), + jsx: javascript({ jsx: true }), + sql: sql(), + ts: javascript({ typescript: true }), + tsx: javascript({ jsx: true, typescript: true }), + vue: vue(), + xml: xml(), +}; + +export type LanguageSupport = keyof typeof languageSupportMap; diff --git a/packages/effects/common-ui/src/components/code-mirror/index.ts b/packages/effects/common-ui/src/components/code-mirror/index.ts new file mode 100644 index 0000000..c9c7c65 --- /dev/null +++ b/packages/effects/common-ui/src/components/code-mirror/index.ts @@ -0,0 +1,2 @@ +export { default as CodeMirror } from './code-mirror.vue'; +export type { LanguageSupport } from './data'; diff --git a/packages/effects/common-ui/src/components/col-page/col-page.vue b/packages/effects/common-ui/src/components/col-page/col-page.vue new file mode 100644 index 0000000..c3f9d68 --- /dev/null +++ b/packages/effects/common-ui/src/components/col-page/col-page.vue @@ -0,0 +1,107 @@ + + diff --git a/packages/effects/common-ui/src/components/col-page/index.ts b/packages/effects/common-ui/src/components/col-page/index.ts new file mode 100644 index 0000000..11b3305 --- /dev/null +++ b/packages/effects/common-ui/src/components/col-page/index.ts @@ -0,0 +1,2 @@ +export { default as ColPage } from './col-page.vue'; +export * from './types'; diff --git a/packages/effects/common-ui/src/components/col-page/types.ts b/packages/effects/common-ui/src/components/col-page/types.ts new file mode 100644 index 0000000..491d1e6 --- /dev/null +++ b/packages/effects/common-ui/src/components/col-page/types.ts @@ -0,0 +1,26 @@ +import type { PageProps } from '../page/types'; + +export interface ColPageProps extends PageProps { + /** + * 左侧宽度 + * @default 30 + */ + leftWidth?: number; + leftMinWidth?: number; + leftMaxWidth?: number; + leftCollapsedWidth?: number; + leftCollapsible?: boolean; + /** + * 右侧宽度 + * @default 70 + */ + rightWidth?: number; + rightMinWidth?: number; + rightCollapsedWidth?: number; + rightMaxWidth?: number; + rightCollapsible?: boolean; + + resizable?: boolean; + splitLine?: boolean; + splitHandle?: boolean; +} diff --git a/packages/effects/common-ui/src/components/ellipsis-text/ellipsis-text.vue b/packages/effects/common-ui/src/components/ellipsis-text/ellipsis-text.vue new file mode 100644 index 0000000..b32c5df --- /dev/null +++ b/packages/effects/common-ui/src/components/ellipsis-text/ellipsis-text.vue @@ -0,0 +1,148 @@ + + + + diff --git a/packages/effects/common-ui/src/components/ellipsis-text/index.ts b/packages/effects/common-ui/src/components/ellipsis-text/index.ts new file mode 100644 index 0000000..67a236c --- /dev/null +++ b/packages/effects/common-ui/src/components/ellipsis-text/index.ts @@ -0,0 +1 @@ +export { default as EllipsisText } from './ellipsis-text.vue'; diff --git a/packages/effects/common-ui/src/components/icon-picker/icon-picker.vue b/packages/effects/common-ui/src/components/icon-picker/icon-picker.vue new file mode 100644 index 0000000..8ac6581 --- /dev/null +++ b/packages/effects/common-ui/src/components/icon-picker/icon-picker.vue @@ -0,0 +1,304 @@ + + diff --git a/packages/effects/common-ui/src/components/icon-picker/icons.ts b/packages/effects/common-ui/src/components/icon-picker/icons.ts new file mode 100644 index 0000000..c72c4c2 --- /dev/null +++ b/packages/effects/common-ui/src/components/icon-picker/icons.ts @@ -0,0 +1,56 @@ +import type { Recordable } from '@vben/types'; + +/** + * 一个缓存对象,在不刷新页面时,无需重复请求远程接口 + */ +export const ICONS_MAP: Recordable = {}; + +interface IconifyResponse { + prefix: string; + total: number; + title: string; + uncategorized?: string[]; + categories?: Recordable; + aliases?: Recordable; +} + +const PENDING_REQUESTS: Recordable> = {}; + +/** + * 通过Iconify接口获取图标集数据。 + * 同一时间多个图标选择器同时请求同一个图标集时,实际上只会发起一次请求(所有请求共享同一份结果)。 + * 请求结果会被缓存,刷新页面前同一个图标集不会再次请求 + * @param prefix 图标集名称 + * @returns 图标集中包含的所有图标名称 + */ +export async function fetchIconsData(prefix: string): Promise { + if (Reflect.has(ICONS_MAP, prefix) && ICONS_MAP[prefix]) { + return ICONS_MAP[prefix]; + } + if (Reflect.has(PENDING_REQUESTS, prefix) && PENDING_REQUESTS[prefix]) { + return PENDING_REQUESTS[prefix]; + } + PENDING_REQUESTS[prefix] = (async () => { + try { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 1000 * 10); + const response: IconifyResponse = await fetch( + `http://http://53.1.252.95:8080/collection?prefix=${prefix}`, + { signal: controller.signal }, + ).then((res) => res.json()); + clearTimeout(timeoutId); + const list = response.uncategorized || []; + if (response.categories) { + for (const category in response.categories) { + list.push(...(response.categories[category] || [])); + } + } + ICONS_MAP[prefix] = list.map((v) => `${prefix}:${v}`); + } catch (error) { + console.error(`Failed to fetch icons for prefix ${prefix}:`, error); + return [] as string[]; + } + return ICONS_MAP[prefix]; + })(); + return PENDING_REQUESTS[prefix]; +} diff --git a/packages/effects/common-ui/src/components/icon-picker/index.ts b/packages/effects/common-ui/src/components/icon-picker/index.ts new file mode 100644 index 0000000..3dabc86 --- /dev/null +++ b/packages/effects/common-ui/src/components/icon-picker/index.ts @@ -0,0 +1 @@ +export { default as IconPicker } from './icon-picker.vue'; diff --git a/packages/effects/common-ui/src/components/index.ts b/packages/effects/common-ui/src/components/index.ts new file mode 100644 index 0000000..dec3049 --- /dev/null +++ b/packages/effects/common-ui/src/components/index.ts @@ -0,0 +1,25 @@ +export * from './api-component'; +export * from './captcha'; +export * from './code-mirror'; +export * from './col-page'; +export * from './ellipsis-text'; +export * from './icon-picker'; +export * from './json-preview'; +export * from './markdown'; +export * from './page'; +export * from './resize'; +export * from './tippy'; +export * from '@vben-core/form-ui'; +export * from '@vben-core/popup-ui'; + +// 给文档用 +export { + VbenButton, + VbenCountToAnimator, + VbenInputPassword, + VbenLoading, + VbenPinInput, + VbenSpinner, +} from '@vben-core/shadcn-ui'; + +export { globalShareState } from '@vben-core/shared/global-state'; diff --git a/packages/effects/common-ui/src/components/json-preview/index.ts b/packages/effects/common-ui/src/components/json-preview/index.ts new file mode 100644 index 0000000..332d22c --- /dev/null +++ b/packages/effects/common-ui/src/components/json-preview/index.ts @@ -0,0 +1 @@ +export { default as JsonPreview } from './json-preview.vue'; diff --git a/packages/effects/common-ui/src/components/json-preview/json-preview.vue b/packages/effects/common-ui/src/components/json-preview/json-preview.vue new file mode 100644 index 0000000..58c7aaa --- /dev/null +++ b/packages/effects/common-ui/src/components/json-preview/json-preview.vue @@ -0,0 +1,19 @@ + + + + + diff --git a/packages/effects/common-ui/src/components/markdown/editor.vue b/packages/effects/common-ui/src/components/markdown/editor.vue new file mode 100644 index 0000000..47ffbe0 --- /dev/null +++ b/packages/effects/common-ui/src/components/markdown/editor.vue @@ -0,0 +1,136 @@ + + + diff --git a/packages/effects/common-ui/src/components/markdown/index.ts b/packages/effects/common-ui/src/components/markdown/index.ts new file mode 100644 index 0000000..dfea14d --- /dev/null +++ b/packages/effects/common-ui/src/components/markdown/index.ts @@ -0,0 +1,2 @@ +export { default as MarkdownEditor } from './editor.vue'; +export { default as MarkdownPreviewer } from './preview.vue'; diff --git a/packages/effects/common-ui/src/components/markdown/preview.vue b/packages/effects/common-ui/src/components/markdown/preview.vue new file mode 100644 index 0000000..3ead292 --- /dev/null +++ b/packages/effects/common-ui/src/components/markdown/preview.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/packages/effects/common-ui/src/components/page/__tests__/page.test.ts b/packages/effects/common-ui/src/components/page/__tests__/page.test.ts new file mode 100644 index 0000000..776fe6a --- /dev/null +++ b/packages/effects/common-ui/src/components/page/__tests__/page.test.ts @@ -0,0 +1,88 @@ +import { mount } from '@vue/test-utils'; +import { describe, expect, it } from 'vitest'; + +import { Page } from '..'; + +describe('page.vue', () => { + it('renders title when passed', () => { + const wrapper = mount(Page, { + props: { + title: 'Test Title', + }, + }); + + expect(wrapper.text()).toContain('Test Title'); + }); + + it('renders description when passed', () => { + const wrapper = mount(Page, { + props: { + description: 'Test Description', + }, + }); + + expect(wrapper.text()).toContain('Test Description'); + }); + + it('renders default slot content', () => { + const wrapper = mount(Page, { + slots: { + default: '

    Default Slot Content

    ', + }, + }); + + expect(wrapper.html()).toContain('

    Default Slot Content

    '); + }); + + it('renders footer slot when showFooter is true', () => { + const wrapper = mount(Page, { + props: { + showFooter: true, + }, + slots: { + footer: '

    Footer Slot Content

    ', + }, + }); + + expect(wrapper.html()).toContain('

    Footer Slot Content

    '); + }); + + it('applies the custom contentClass', () => { + const wrapper = mount(Page, { + props: { + contentClass: 'custom-class', + }, + }); + + const contentDiv = wrapper.find('.p-4'); + expect(contentDiv.classes()).toContain('custom-class'); + }); + + it('does not render title slot if title prop is provided', () => { + const wrapper = mount(Page, { + props: { + title: 'Test Title', + }, + slots: { + title: '

    Title Slot Content

    ', + }, + }); + + expect(wrapper.text()).toContain('Title Slot Content'); + expect(wrapper.html()).not.toContain('Test Title'); + }); + + it('does not render description slot if description prop is provided', () => { + const wrapper = mount(Page, { + props: { + description: 'Test Description', + }, + slots: { + description: '

    Description Slot Content

    ', + }, + }); + + expect(wrapper.text()).toContain('Description Slot Content'); + expect(wrapper.html()).not.toContain('Test Description'); + }); +}); diff --git a/packages/effects/common-ui/src/components/page/index.ts b/packages/effects/common-ui/src/components/page/index.ts new file mode 100644 index 0000000..fd9e02f --- /dev/null +++ b/packages/effects/common-ui/src/components/page/index.ts @@ -0,0 +1,2 @@ +export { default as Page } from './page.vue'; +export * from './types'; diff --git a/packages/effects/common-ui/src/components/page/page.vue b/packages/effects/common-ui/src/components/page/page.vue new file mode 100644 index 0000000..a2c1f6d --- /dev/null +++ b/packages/effects/common-ui/src/components/page/page.vue @@ -0,0 +1,104 @@ + + + diff --git a/packages/effects/common-ui/src/components/page/types.ts b/packages/effects/common-ui/src/components/page/types.ts new file mode 100644 index 0000000..5316d08 --- /dev/null +++ b/packages/effects/common-ui/src/components/page/types.ts @@ -0,0 +1,11 @@ +export interface PageProps { + title?: string; + description?: string; + contentClass?: string; + /** + * 根据content可见高度自适应 + */ + autoContentHeight?: boolean; + headerClass?: string; + footerClass?: string; +} diff --git a/packages/effects/common-ui/src/components/resize/index.ts b/packages/effects/common-ui/src/components/resize/index.ts new file mode 100644 index 0000000..f937522 --- /dev/null +++ b/packages/effects/common-ui/src/components/resize/index.ts @@ -0,0 +1 @@ +export { default as VResize } from './resize.vue'; diff --git a/packages/effects/common-ui/src/components/resize/resize.vue b/packages/effects/common-ui/src/components/resize/resize.vue new file mode 100644 index 0000000..aaf89ea --- /dev/null +++ b/packages/effects/common-ui/src/components/resize/resize.vue @@ -0,0 +1,1122 @@ + + + + + diff --git a/packages/effects/common-ui/src/components/tippy/directive.ts b/packages/effects/common-ui/src/components/tippy/directive.ts new file mode 100644 index 0000000..ed277d6 --- /dev/null +++ b/packages/effects/common-ui/src/components/tippy/directive.ts @@ -0,0 +1,100 @@ +import type { ComputedRef, Directive } from 'vue'; + +import { useTippy } from 'vue-tippy'; + +export default function useTippyDirective(isDark: ComputedRef) { + const directive: Directive = { + mounted(el, binding, vnode) { + const opts = + typeof binding.value === 'string' + ? { content: binding.value } + : binding.value || {}; + + const modifiers = Object.keys(binding.modifiers || {}); + const placement = modifiers.find((modifier) => modifier !== 'arrow'); + const withArrow = modifiers.includes('arrow'); + + if (placement) { + opts.placement = opts.placement || placement; + } + + if (withArrow) { + opts.arrow = opts.arrow === undefined ? true : opts.arrow; + } + + if (vnode.props && vnode.props.onTippyShow) { + opts.onShow = function (...args: any[]) { + return vnode.props?.onTippyShow(...args); + }; + } + + if (vnode.props && vnode.props.onTippyShown) { + opts.onShown = function (...args: any[]) { + return vnode.props?.onTippyShown(...args); + }; + } + + if (vnode.props && vnode.props.onTippyHidden) { + opts.onHidden = function (...args: any[]) { + return vnode.props?.onTippyHidden(...args); + }; + } + + if (vnode.props && vnode.props.onTippyHide) { + opts.onHide = function (...args: any[]) { + return vnode.props?.onTippyHide(...args); + }; + } + + if (vnode.props && vnode.props.onTippyMount) { + opts.onMount = function (...args: any[]) { + return vnode.props?.onTippyMount(...args); + }; + } + + if (el.getAttribute('title') && !opts.content) { + opts.content = el.getAttribute('title'); + el.removeAttribute('title'); + } + + if (el.getAttribute('content') && !opts.content) { + opts.content = el.getAttribute('content'); + } + + useTippy(el, opts); + }, + unmounted(el) { + if (el.$tippy) { + el.$tippy.destroy(); + } else if (el._tippy) { + el._tippy.destroy(); + } + }, + + updated(el, binding) { + const opts = + typeof binding.value === 'string' + ? { content: binding.value, theme: isDark.value ? '' : 'light' } + : Object.assign( + { theme: isDark.value ? '' : 'light' }, + binding.value, + ); + + if (el.getAttribute('title') && !opts.content) { + opts.content = el.getAttribute('title'); + el.removeAttribute('title'); + } + + if (el.getAttribute('content') && !opts.content) { + opts.content = el.getAttribute('content'); + } + + if (el.$tippy) { + el.$tippy.setProps(opts || {}); + } else if (el._tippy) { + el._tippy.setProps(opts || {}); + } + }, + }; + return directive; +} diff --git a/packages/effects/common-ui/src/components/tippy/index.ts b/packages/effects/common-ui/src/components/tippy/index.ts new file mode 100644 index 0000000..6080e58 --- /dev/null +++ b/packages/effects/common-ui/src/components/tippy/index.ts @@ -0,0 +1,67 @@ +import type { DefaultProps, Props } from 'tippy.js'; + +import type { App, SetupContext } from 'vue'; + +import { h, watchEffect } from 'vue'; +import { setDefaultProps, Tippy as TippyComponent } from 'vue-tippy'; + +import { usePreferences } from '@vben-core/preferences'; + +import useTippyDirective from './directive'; + +import 'tippy.js/dist/tippy.css'; +import 'tippy.js/dist/backdrop.css'; +import 'tippy.js/themes/light.css'; +import 'tippy.js/animations/scale.css'; +import 'tippy.js/animations/shift-toward.css'; +import 'tippy.js/animations/shift-away.css'; +import 'tippy.js/animations/perspective.css'; + +const { isDark } = usePreferences(); +export type TippyProps = Partial< + Props & { + animation?: + | 'fade' + | 'perspective' + | 'scale' + | 'shift-away' + | 'shift-toward' + | boolean; + theme?: 'auto' | 'dark' | 'light'; + } +>; + +export function initTippy(app: App, options?: DefaultProps) { + setDefaultProps({ + allowHTML: true, + delay: [500, 200], + theme: isDark.value ? '' : 'light', + ...options, + }); + if (!options || !Reflect.has(options, 'theme') || options.theme === 'auto') { + watchEffect(() => { + setDefaultProps({ theme: isDark.value ? '' : 'light' }); + }); + } + + app.directive('tippy', useTippyDirective(isDark)); +} + +export const Tippy = (props: any, { attrs, slots }: SetupContext) => { + let theme: string = (attrs.theme as string) ?? 'auto'; + if (theme === 'auto') { + theme = isDark.value ? '' : 'light'; + } + if (theme === 'dark') { + theme = ''; + } + return h( + TippyComponent, + { + ...props, + ...attrs, + theme, + }, + slots, + ); +}; diff --git a/packages/effects/common-ui/src/index.ts b/packages/effects/common-ui/src/index.ts new file mode 100644 index 0000000..ac4f4e4 --- /dev/null +++ b/packages/effects/common-ui/src/index.ts @@ -0,0 +1,3 @@ +export * from './components'; +export * from './ui'; +export { VbenAvatar } from '@vben-core/shadcn-ui'; diff --git a/packages/effects/common-ui/src/ui/about/about.ts b/packages/effects/common-ui/src/ui/about/about.ts new file mode 100644 index 0000000..c4cf957 --- /dev/null +++ b/packages/effects/common-ui/src/ui/about/about.ts @@ -0,0 +1,14 @@ +import type { Component } from 'vue'; + +interface AboutProps { + description?: string; + name?: string; + title?: string; +} + +interface DescriptionItem { + content: Component | string; + title: string; +} + +export type { AboutProps, DescriptionItem }; diff --git a/packages/effects/common-ui/src/ui/about/about.vue b/packages/effects/common-ui/src/ui/about/about.vue new file mode 100644 index 0000000..74b3a1d --- /dev/null +++ b/packages/effects/common-ui/src/ui/about/about.vue @@ -0,0 +1,181 @@ + + + diff --git a/packages/effects/common-ui/src/ui/about/index.ts b/packages/effects/common-ui/src/ui/about/index.ts new file mode 100644 index 0000000..5860f5b --- /dev/null +++ b/packages/effects/common-ui/src/ui/about/index.ts @@ -0,0 +1 @@ +export { default as About } from './about.vue'; diff --git a/packages/effects/common-ui/src/ui/authentication/auth-title.vue b/packages/effects/common-ui/src/ui/authentication/auth-title.vue new file mode 100644 index 0000000..3ed9a6b --- /dev/null +++ b/packages/effects/common-ui/src/ui/authentication/auth-title.vue @@ -0,0 +1,13 @@ + diff --git a/packages/effects/common-ui/src/ui/authentication/code-login.vue b/packages/effects/common-ui/src/ui/authentication/code-login.vue new file mode 100644 index 0000000..51ff2f3 --- /dev/null +++ b/packages/effects/common-ui/src/ui/authentication/code-login.vue @@ -0,0 +1,117 @@ + + + diff --git a/packages/effects/common-ui/src/ui/authentication/forget-password.vue b/packages/effects/common-ui/src/ui/authentication/forget-password.vue new file mode 100644 index 0000000..0b67046 --- /dev/null +++ b/packages/effects/common-ui/src/ui/authentication/forget-password.vue @@ -0,0 +1,114 @@ + + + diff --git a/packages/effects/common-ui/src/ui/authentication/index.ts b/packages/effects/common-ui/src/ui/authentication/index.ts new file mode 100644 index 0000000..dcd05b7 --- /dev/null +++ b/packages/effects/common-ui/src/ui/authentication/index.ts @@ -0,0 +1,12 @@ +export { default as AuthenticationCodeLogin } from './code-login.vue'; +export { default as AuthenticationForgetPassword } from './forget-password.vue'; +export { default as AuthenticationLogin } from './login.vue'; +export { default as AuthenticationLoginExpiredModal } from './login-expired-modal.vue'; +export { default as AuthenticationQrCodeLogin } from './qrcode-login.vue'; +export { default as AuthenticationRegister } from './register.vue'; +export type { + AuthenticationProps, + GrantType, + LoginAndRegisterParams, + LoginCodeParams, +} from './types'; diff --git a/packages/effects/common-ui/src/ui/authentication/login-expired-modal.vue b/packages/effects/common-ui/src/ui/authentication/login-expired-modal.vue new file mode 100644 index 0000000..4b4f9b4 --- /dev/null +++ b/packages/effects/common-ui/src/ui/authentication/login-expired-modal.vue @@ -0,0 +1,79 @@ + + + diff --git a/packages/effects/common-ui/src/ui/authentication/login.vue b/packages/effects/common-ui/src/ui/authentication/login.vue new file mode 100644 index 0000000..7555a9a --- /dev/null +++ b/packages/effects/common-ui/src/ui/authentication/login.vue @@ -0,0 +1,187 @@ + + + diff --git a/packages/effects/common-ui/src/ui/authentication/qrcode-login.vue b/packages/effects/common-ui/src/ui/authentication/qrcode-login.vue new file mode 100644 index 0000000..aab2e55 --- /dev/null +++ b/packages/effects/common-ui/src/ui/authentication/qrcode-login.vue @@ -0,0 +1,92 @@ + + + diff --git a/packages/effects/common-ui/src/ui/authentication/register.vue b/packages/effects/common-ui/src/ui/authentication/register.vue new file mode 100644 index 0000000..8249331 --- /dev/null +++ b/packages/effects/common-ui/src/ui/authentication/register.vue @@ -0,0 +1,118 @@ + + + diff --git a/packages/effects/common-ui/src/ui/authentication/third-party-login.vue b/packages/effects/common-ui/src/ui/authentication/third-party-login.vue new file mode 100644 index 0000000..98afc9a --- /dev/null +++ b/packages/effects/common-ui/src/ui/authentication/third-party-login.vue @@ -0,0 +1,43 @@ + + + diff --git a/packages/effects/common-ui/src/ui/authentication/types.ts b/packages/effects/common-ui/src/ui/authentication/types.ts new file mode 100644 index 0000000..0e4288f --- /dev/null +++ b/packages/effects/common-ui/src/ui/authentication/types.ts @@ -0,0 +1,117 @@ +interface AuthenticationProps { + /** + * @zh_CN 验证码登录路径 + */ + codeLoginPath?: string; + + /** + * @zh_CN 忘记密码路径 + */ + forgetPasswordPath?: string; + + /** + * @zh_CN 是否处于加载处理状态 + */ + loading?: boolean; + + /** + * @zh_CN 二维码登录路径 + */ + qrCodeLoginPath?: string; + + /** + * @zh_CN 注册路径 + */ + registerPath?: string; + + /** + * @zh_CN 是否显示验证码登录 + */ + showCodeLogin?: boolean; + + /** + * @zh_CN 是否显示忘记密码 + */ + showForgetPassword?: boolean; + + /** + * @zh_CN 是否显示二维码登录 + */ + showQrcodeLogin?: boolean; + + /** + * @zh_CN 是否显示注册按钮 + */ + showRegister?: boolean; + + /** + * @zh_CN 是否显示记住账号 + */ + showRememberMe?: boolean; + + /** + * @zh_CN 是否显示第三方登录 + */ + showThirdPartyLogin?: boolean; + + /** + * @zh_CN 登录框子标题 + */ + subTitle?: string; + + /** + * @zh_CN 登录框标题 + */ + title?: string; + /** + * @zh_CN 提交按钮文本 + */ + submitButtonText?: string; +} + +/** + * 登录类型 + * password 密码 + * sms 短信 + * social 第三方oauth + * email 邮箱 + * xcx 小程序 + */ +type GrantType = 'email' | 'password' | 'sms' | 'social' | 'xcx'; + +interface LoginAndRegisterParams { + code?: string; + grantType: GrantType; + password: string; + tenantId: string; + username: string; + uuid?: string; +} + +interface LoginCodeParams { + tenantId: string; + code: string; + phoneNumber: string; +} + +interface LoginEmits { + submit: [LoginAndRegisterParams]; +} + +interface LoginCodeEmits { + submit: [LoginCodeParams]; +} + +interface RegisterEmits { + submit: [LoginAndRegisterParams]; +} + +export type { + AuthenticationProps, + GrantType, + LoginAndRegisterParams, + LoginCodeEmits, + LoginCodeParams, + LoginEmits, + RegisterEmits, +}; diff --git a/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-chart-card.vue b/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-chart-card.vue new file mode 100644 index 0000000..8a23449 --- /dev/null +++ b/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-chart-card.vue @@ -0,0 +1,31 @@ + + + + diff --git a/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-charts-tabs.vue b/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-charts-tabs.vue new file mode 100644 index 0000000..9c8cf20 --- /dev/null +++ b/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-charts-tabs.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-overview.vue b/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-overview.vue new file mode 100644 index 0000000..f07afa4 --- /dev/null +++ b/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-overview.vue @@ -0,0 +1,86 @@ + + + + diff --git a/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-pie-tabs.vue b/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-pie-tabs.vue new file mode 100644 index 0000000..9c8cf20 --- /dev/null +++ b/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-pie-tabs.vue @@ -0,0 +1,40 @@ + + + diff --git a/packages/effects/common-ui/src/ui/dashboard/analysis/analytics-task-statistics copy.vue b/packages/effects/common-ui/src/ui/dashboard/analysis/analytics-task-statistics copy.vue new file mode 100644 index 0000000..ab340b9 --- /dev/null +++ b/packages/effects/common-ui/src/ui/dashboard/analysis/analytics-task-statistics copy.vue @@ -0,0 +1,310 @@ + + + + diff --git a/packages/effects/common-ui/src/ui/dashboard/analysis/analytics-task-statistics.vue b/packages/effects/common-ui/src/ui/dashboard/analysis/analytics-task-statistics.vue new file mode 100644 index 0000000..7fba523 --- /dev/null +++ b/packages/effects/common-ui/src/ui/dashboard/analysis/analytics-task-statistics.vue @@ -0,0 +1,442 @@ + + + + + diff --git a/packages/effects/common-ui/src/ui/dashboard/analysis/index.ts b/packages/effects/common-ui/src/ui/dashboard/analysis/index.ts new file mode 100644 index 0000000..a3cfc2e --- /dev/null +++ b/packages/effects/common-ui/src/ui/dashboard/analysis/index.ts @@ -0,0 +1,12 @@ +/* + * @Author: hcc han_chen301@163.com + * @Date: 2025-05-23 09:27:20 + * @LastEditors: hcc + * @LastEditTime: 2025-05-29 11:19:06 + * @remarker: + */ +export { default as AnalysisChartCard } from './analysis-chart-card.vue'; +export { default as AnalysisChartsTabs } from './analysis-charts-tabs.vue'; +export { default as AnalysisOverview } from './analysis-overview.vue'; +export { default as AnalysisPieTabs } from './analysis-pie-tabs.vue'; +export { default as AnalyticsTaskStatistics } from './analytics-task-statistics.vue'; diff --git a/packages/effects/common-ui/src/ui/dashboard/index.ts b/packages/effects/common-ui/src/ui/dashboard/index.ts new file mode 100644 index 0000000..bc74424 --- /dev/null +++ b/packages/effects/common-ui/src/ui/dashboard/index.ts @@ -0,0 +1,3 @@ +export * from './analysis'; +export type * from './typing'; +export * from './workbench'; diff --git a/packages/effects/common-ui/src/ui/dashboard/typing.ts b/packages/effects/common-ui/src/ui/dashboard/typing.ts new file mode 100644 index 0000000..995d3d3 --- /dev/null +++ b/packages/effects/common-ui/src/ui/dashboard/typing.ts @@ -0,0 +1,74 @@ +/* + * @Author: hcc han_chen301@163.com + * @Date: 2025-05-23 09:27:20 + * @LastEditors: hcc + * @LastEditTime: 2025-06-13 16:10:31 + * @remarker: + */ +import type { Component } from 'vue'; + +interface AnalysisOverviewItem { + icon: Component | string; + title: string; + totalTitle: string; + totalValue: number; + value: number; +} + +interface AnalysisBaseItem { + icon: Component | string; + title: string; + value: number | string; + key?: string; +} + +interface AnalysisTaskStatisticsItem { + icon: Component | string; + title: string; + totalTitle: string; + totalValue: number; + value: number; + children?: AnalysisBaseItem[]; +} + +interface WorkbenchProjectItem { + color?: string; + content: string; + date: string; + group: string; + icon: Component | string; + title: string; + url?: string; +} + +interface WorkbenchTrendItem { + avatar: string; + content: string; + date: string; + title: string; +} + +interface WorkbenchTodoItem { + completed?: boolean; + content?: string; + date?: string; + title?: string; + type?: string; + task_no?: string; +} + +interface WorkbenchQuickNavItem { + color?: string; + icon: Component | string; + title: string; + url?: string; +} + +export type { + AnalysisOverviewItem, + AnalysisTaskStatisticsItem, + WorkbenchProjectItem, + WorkbenchQuickNavItem, + WorkbenchTodoItem, + WorkbenchTrendItem, +}; diff --git a/packages/effects/common-ui/src/ui/dashboard/workbench/index.ts b/packages/effects/common-ui/src/ui/dashboard/workbench/index.ts new file mode 100644 index 0000000..76b7af1 --- /dev/null +++ b/packages/effects/common-ui/src/ui/dashboard/workbench/index.ts @@ -0,0 +1,5 @@ +export { default as WorkbenchHeader } from './workbench-header.vue'; +export { default as WorkbenchProject } from './workbench-project.vue'; +export { default as WorkbenchQuickNav } from './workbench-quick-nav.vue'; +export { default as WorkbenchTodo } from './workbench-todo.vue'; +export { default as WorkbenchTrends } from './workbench-trends.vue'; diff --git a/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-header.vue b/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-header.vue new file mode 100644 index 0000000..a3ea3eb --- /dev/null +++ b/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-header.vue @@ -0,0 +1,46 @@ + + diff --git a/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-project.vue b/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-project.vue new file mode 100644 index 0000000..0a3fb7c --- /dev/null +++ b/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-project.vue @@ -0,0 +1,63 @@ + + + diff --git a/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-quick-nav.vue b/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-quick-nav.vue new file mode 100644 index 0000000..495c7b4 --- /dev/null +++ b/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-quick-nav.vue @@ -0,0 +1,49 @@ + + + diff --git a/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-todo.vue b/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-todo.vue new file mode 100644 index 0000000..8aae81fe --- /dev/null +++ b/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-todo.vue @@ -0,0 +1,63 @@ + + + diff --git a/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-trends.vue b/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-trends.vue new file mode 100644 index 0000000..358b210 --- /dev/null +++ b/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-trends.vue @@ -0,0 +1,64 @@ + + + diff --git a/packages/effects/common-ui/src/ui/fallback/fallback.ts b/packages/effects/common-ui/src/ui/fallback/fallback.ts new file mode 100644 index 0000000..5f8e193 --- /dev/null +++ b/packages/effects/common-ui/src/ui/fallback/fallback.ts @@ -0,0 +1,25 @@ +interface FallbackProps { + /** + * 描述 + */ + description?: string; + /** + * @zh_CN 首页路由地址 + * @default / + */ + homePath?: string; + /** + * @zh_CN 默认显示的图片 + * @default pageNotFoundSvg + */ + image?: string; + /** + * @zh_CN 内置类型 + */ + status?: '403' | '404' | '500' | 'coming-soon' | 'offline'; + /** + * @zh_CN 页面提示语 + */ + title?: string; +} +export type { FallbackProps }; diff --git a/packages/effects/common-ui/src/ui/fallback/fallback.vue b/packages/effects/common-ui/src/ui/fallback/fallback.vue new file mode 100644 index 0000000..e4f3a28 --- /dev/null +++ b/packages/effects/common-ui/src/ui/fallback/fallback.vue @@ -0,0 +1,162 @@ + + + diff --git a/packages/effects/common-ui/src/ui/fallback/icons/icon-403.vue b/packages/effects/common-ui/src/ui/fallback/icons/icon-403.vue new file mode 100644 index 0000000..e390867 --- /dev/null +++ b/packages/effects/common-ui/src/ui/fallback/icons/icon-403.vue @@ -0,0 +1,151 @@ + diff --git a/packages/effects/common-ui/src/ui/fallback/icons/icon-404.vue b/packages/effects/common-ui/src/ui/fallback/icons/icon-404.vue new file mode 100644 index 0000000..385cf6a --- /dev/null +++ b/packages/effects/common-ui/src/ui/fallback/icons/icon-404.vue @@ -0,0 +1,154 @@ + diff --git a/packages/effects/common-ui/src/ui/fallback/icons/icon-500.vue b/packages/effects/common-ui/src/ui/fallback/icons/icon-500.vue new file mode 100644 index 0000000..97326b7 --- /dev/null +++ b/packages/effects/common-ui/src/ui/fallback/icons/icon-500.vue @@ -0,0 +1,215 @@ + diff --git a/packages/effects/common-ui/src/ui/fallback/icons/icon-coming-soon.vue b/packages/effects/common-ui/src/ui/fallback/icons/icon-coming-soon.vue new file mode 100644 index 0000000..e511c43 --- /dev/null +++ b/packages/effects/common-ui/src/ui/fallback/icons/icon-coming-soon.vue @@ -0,0 +1,262 @@ + diff --git a/packages/effects/common-ui/src/ui/fallback/icons/icon-offline.vue b/packages/effects/common-ui/src/ui/fallback/icons/icon-offline.vue new file mode 100644 index 0000000..9e50b19 --- /dev/null +++ b/packages/effects/common-ui/src/ui/fallback/icons/icon-offline.vue @@ -0,0 +1,112 @@ + diff --git a/packages/effects/common-ui/src/ui/fallback/icons/warning.svg b/packages/effects/common-ui/src/ui/fallback/icons/warning.svg new file mode 100644 index 0000000..951a905 --- /dev/null +++ b/packages/effects/common-ui/src/ui/fallback/icons/warning.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/effects/common-ui/src/ui/fallback/index.ts b/packages/effects/common-ui/src/ui/fallback/index.ts new file mode 100644 index 0000000..f56f665 --- /dev/null +++ b/packages/effects/common-ui/src/ui/fallback/index.ts @@ -0,0 +1,2 @@ +export type * from './fallback'; +export { default as Fallback } from './fallback.vue'; diff --git a/packages/effects/common-ui/src/ui/index.ts b/packages/effects/common-ui/src/ui/index.ts new file mode 100644 index 0000000..fb99fde --- /dev/null +++ b/packages/effects/common-ui/src/ui/index.ts @@ -0,0 +1,4 @@ +export * from './about'; +export * from './authentication'; +export * from './dashboard'; +export * from './fallback'; diff --git a/packages/effects/common-ui/tsconfig.json b/packages/effects/common-ui/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/packages/effects/common-ui/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/effects/hooks/README.md b/packages/effects/hooks/README.md new file mode 100644 index 0000000..54d9ff7 --- /dev/null +++ b/packages/effects/hooks/README.md @@ -0,0 +1,19 @@ +# @vben/hooks + +用于多个 `app` 公用的 hook,继承了 `@vben/hooks` 的所有能力。业务上有通用 hooks 可以放在这里。 + +## 用法 + +### 添加依赖 + +```bash +# 进入目标应用目录,例如 apps/xxxx-app +# cd apps/xxxx-app +pnpm add @vben/hooks +``` + +### 使用 + +```ts +import { useNamespace } from '@vben/hooks'; +``` diff --git a/packages/effects/hooks/package.json b/packages/effects/hooks/package.json new file mode 100644 index 0000000..c9120d2 --- /dev/null +++ b/packages/effects/hooks/package.json @@ -0,0 +1,33 @@ +{ + "name": "@vben/hooks", + "version": "5.5.3", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/effects/hooks" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben-core/composables": "workspace:*", + "@vben/preferences": "workspace:*", + "@vben/stores": "workspace:*", + "@vben/types": "workspace:*", + "@vben/utils": "workspace:*", + "@vueuse/core": "catalog:", + "vue": "catalog:", + "vue-router": "catalog:", + "watermark-js-plus": "catalog:" + } +} diff --git a/packages/effects/hooks/src/index.ts b/packages/effects/hooks/src/index.ts new file mode 100644 index 0000000..c189fd7 --- /dev/null +++ b/packages/effects/hooks/src/index.ts @@ -0,0 +1,9 @@ +export * from './use-app-config'; +export * from './use-content-maximize'; +export * from './use-design-tokens'; +export * from './use-hover-toggle'; +export * from './use-pagination'; +export * from './use-refresh'; +export * from './use-tabs'; +export * from './use-watermark'; +export * from '@vben-core/composables'; diff --git a/packages/effects/hooks/src/use-app-config.ts b/packages/effects/hooks/src/use-app-config.ts new file mode 100644 index 0000000..6cf497e --- /dev/null +++ b/packages/effects/hooks/src/use-app-config.ts @@ -0,0 +1,40 @@ +import type { + ApplicationConfig, + VbenAdminProAppConfigRaw, +} from '@vben/types/global'; + +/** + * 由 vite-inject-app-config 注入的全局配置 + */ +export function useAppConfig( + env: Record, + isProduction: boolean, +): ApplicationConfig { + // 生产环境下,直接使用 window._VBEN_ADMIN_PRO_APP_CONF_ 全局变量 + const config = isProduction + ? window._VBEN_ADMIN_PRO_APP_CONF_ + : (env as VbenAdminProAppConfigRaw); + + const { + VITE_GLOB_API_URL, + VITE_GLOB_APP_CLIENT_ID, + VITE_GLOB_ENABLE_ENCRYPT, + VITE_GLOB_RSA_PRIVATE_KEY, + VITE_GLOB_RSA_PUBLIC_KEY, + VITE_GLOB_SSE_ENABLE, + } = config; + + return { + // 后端地址 + apiURL: VITE_GLOB_API_URL, + // 客户端key + clientId: VITE_GLOB_APP_CLIENT_ID, + enableEncrypt: VITE_GLOB_ENABLE_ENCRYPT === 'true', + // RSA私钥 + rsaPrivateKey: VITE_GLOB_RSA_PRIVATE_KEY, + // RSA公钥 + rsaPublicKey: VITE_GLOB_RSA_PUBLIC_KEY, + // 是否开启sse + sseEnable: VITE_GLOB_SSE_ENABLE === 'true', + }; +} diff --git a/packages/effects/hooks/src/use-content-maximize.ts b/packages/effects/hooks/src/use-content-maximize.ts new file mode 100644 index 0000000..77d1fab --- /dev/null +++ b/packages/effects/hooks/src/use-content-maximize.ts @@ -0,0 +1,24 @@ +import { updatePreferences, usePreferences } from '@vben/preferences'; +/** + * 主体区域最大化 + */ +export function useContentMaximize() { + const { contentIsMaximize } = usePreferences(); + + function toggleMaximize() { + const isMaximize = contentIsMaximize.value; + + updatePreferences({ + header: { + hidden: !isMaximize, + }, + sidebar: { + hidden: !isMaximize, + }, + }); + } + return { + contentIsMaximize, + toggleMaximize, + }; +} diff --git a/packages/effects/hooks/src/use-design-tokens.ts b/packages/effects/hooks/src/use-design-tokens.ts new file mode 100644 index 0000000..e33f721 --- /dev/null +++ b/packages/effects/hooks/src/use-design-tokens.ts @@ -0,0 +1,281 @@ +import { reactive, watch } from 'vue'; + +import { preferences, usePreferences } from '@vben/preferences'; +import { convertToRgb, updateCSSVariables } from '@vben/utils'; + +/** + * 用于适配各个框架的设计系统 + */ + +export function useAntdDesignTokens() { + const rootStyles = getComputedStyle(document.documentElement); + + const tokens = reactive({ + borderRadius: '' as any, + colorBgBase: '', + colorBgContainer: '', + colorBgElevated: '', + colorBgLayout: '', + colorBgMask: '', + colorBorder: '', + colorBorderSecondary: '', + colorError: '', + colorInfo: '', + colorPrimary: '', + colorSuccess: '', + colorTextBase: '', + colorWarning: '', + zIndexPopupBase: 2000, // 调整基础弹层层级,避免下拉等组件被弹窗或者最大化状态下的表格遮挡 + }); + + const getCssVariableValue = (variable: string, isColor: boolean = true) => { + const value = rootStyles.getPropertyValue(variable); + return isColor ? `hsl(${value})` : value; + }; + + watch( + () => preferences.theme, + () => { + tokens.colorPrimary = getCssVariableValue('--primary'); + + tokens.colorInfo = getCssVariableValue('--primary'); + + tokens.colorError = getCssVariableValue('--destructive'); + + tokens.colorWarning = getCssVariableValue('--warning'); + + tokens.colorSuccess = getCssVariableValue('--success'); + + tokens.colorTextBase = getCssVariableValue('--foreground'); + + getCssVariableValue('--primary-foreground'); + + tokens.colorBorderSecondary = tokens.colorBorder = + getCssVariableValue('--border'); + + tokens.colorBgElevated = getCssVariableValue('--popover'); + + tokens.colorBgContainer = getCssVariableValue('--card'); + + tokens.colorBgBase = getCssVariableValue('--background'); + + const radius = Number.parseFloat(getCssVariableValue('--radius', false)); + // 1rem = 16px + tokens.borderRadius = radius * 16; + + tokens.colorBgLayout = getCssVariableValue('--background-deep'); + tokens.colorBgMask = getCssVariableValue('--overlay'); + }, + { immediate: true }, + ); + + return { + tokens, + }; +} + +export function useNaiveDesignTokens() { + const rootStyles = getComputedStyle(document.documentElement); + + const commonTokens = reactive({ + baseColor: '', + bodyColor: '', + borderColor: '', + borderRadius: '', + cardColor: '', + dividerColor: '', + errorColor: '', + errorColorHover: '', + errorColorPressed: '', + errorColorSuppl: '', + invertedColor: '', + modalColor: '', + popoverColor: '', + primaryColor: '', + primaryColorHover: '', + primaryColorPressed: '', + primaryColorSuppl: '', + successColor: '', + successColorHover: '', + successColorPressed: '', + successColorSuppl: '', + tableColor: '', + textColorBase: '', + warningColor: '', + warningColorHover: '', + warningColorPressed: '', + warningColorSuppl: '', + }); + + const getCssVariableValue = (variable: string, isColor: boolean = true) => { + const value = rootStyles.getPropertyValue(variable); + return isColor ? convertToRgb(`hsl(${value})`) : value; + }; + + watch( + () => preferences.theme, + () => { + commonTokens.primaryColor = getCssVariableValue('--primary'); + commonTokens.primaryColorHover = getCssVariableValue('--primary-600'); + commonTokens.primaryColorPressed = getCssVariableValue('--primary-700'); + commonTokens.primaryColorSuppl = getCssVariableValue('--primary-800'); + + commonTokens.errorColor = getCssVariableValue('--destructive'); + commonTokens.errorColorHover = getCssVariableValue('--destructive-600'); + commonTokens.errorColorPressed = getCssVariableValue('--destructive-700'); + commonTokens.errorColorSuppl = getCssVariableValue('--destructive-800'); + + commonTokens.warningColor = getCssVariableValue('--warning'); + commonTokens.warningColorHover = getCssVariableValue('--warning-600'); + commonTokens.warningColorPressed = getCssVariableValue('--warning-700'); + commonTokens.warningColorSuppl = getCssVariableValue('--warning-800'); + + commonTokens.successColor = getCssVariableValue('--success'); + commonTokens.successColorHover = getCssVariableValue('--success-600'); + commonTokens.successColorPressed = getCssVariableValue('--success-700'); + commonTokens.successColorSuppl = getCssVariableValue('--success-800'); + + commonTokens.textColorBase = getCssVariableValue('--foreground'); + + commonTokens.baseColor = getCssVariableValue('--primary-foreground'); + + commonTokens.dividerColor = commonTokens.borderColor = + getCssVariableValue('--border'); + + commonTokens.modalColor = commonTokens.popoverColor = + getCssVariableValue('--popover'); + + commonTokens.tableColor = commonTokens.cardColor = + getCssVariableValue('--card'); + + commonTokens.bodyColor = getCssVariableValue('--background'); + commonTokens.invertedColor = getCssVariableValue('--background-deep'); + + commonTokens.borderRadius = getCssVariableValue('--radius', false); + }, + { immediate: true }, + ); + return { + commonTokens, + }; +} + +export function useElementPlusDesignTokens() { + const { isDark } = usePreferences(); + const rootStyles = getComputedStyle(document.documentElement); + + const getCssVariableValueRaw = (variable: string) => { + return rootStyles.getPropertyValue(variable); + }; + + const getCssVariableValue = (variable: string, isColor: boolean = true) => { + const value = getCssVariableValueRaw(variable); + return isColor ? convertToRgb(`hsl(${value})`) : value; + }; + + watch( + () => preferences.theme, + () => { + const background = getCssVariableValue('--background'); + const border = getCssVariableValue('--border'); + const accent = getCssVariableValue('--accent'); + + const variables: Record = { + '--el-bg-color': background, + '--el-bg-color-overlay': getCssVariableValue('--popover'), + '--el-bg-color-page': getCssVariableValue('--background-deep'), + '--el-border-color': border, + '--el-border-color-dark': border, + '--el-border-color-extra-light': border, + '--el-border-color-hover': accent, + '--el-border-color-light': border, + '--el-border-color-lighter': border, + + '--el-border-radius-base': getCssVariableValue('--radius', false), + '--el-color-danger': getCssVariableValue('--destructive-500'), + '--el-color-danger-dark-2': getCssVariableValue('--destructive'), + '--el-color-danger-light-3': getCssVariableValue('--destructive-400'), + '--el-color-danger-light-5': getCssVariableValue('--destructive-300'), + '--el-color-danger-light-7': getCssVariableValue('--destructive-200'), + '--el-color-danger-light-8': isDark.value + ? border + : getCssVariableValue('--destructive-100'), + '--el-color-danger-light-9': isDark.value + ? accent + : getCssVariableValue('--destructive-50'), + + '--el-color-error': getCssVariableValue('--destructive-500'), + '--el-color-error-dark-2': getCssVariableValue('--destructive'), + '--el-color-error-light-3': getCssVariableValue('--destructive-400'), + '--el-color-error-light-5': getCssVariableValue('--destructive-300'), + '--el-color-error-light-7': getCssVariableValue('--destructive-200'), + '--el-color-error-light-8': isDark.value + ? border + : getCssVariableValue('--destructive-100'), + '--el-color-error-light-9': isDark.value + ? accent + : getCssVariableValue('--destructive-50'), + + '--el-color-info-light-8': border, + '--el-color-info-light-9': getCssVariableValue('--info'), // getCssVariableValue('--secondary'), + '--el-color-primary': getCssVariableValue('--primary-500'), + '--el-color-primary-dark-2': getCssVariableValue('--primary'), + '--el-color-primary-light-3': getCssVariableValue('--primary-400'), + '--el-color-primary-light-5': getCssVariableValue('--primary-300'), + '--el-color-primary-light-7': isDark.value + ? border + : getCssVariableValue('--primary-200'), + '--el-color-primary-light-8': isDark.value + ? border + : getCssVariableValue('--primary-100'), + '--el-color-primary-light-9': isDark.value + ? accent + : getCssVariableValue('--primary-50'), + + '--el-color-success': getCssVariableValue('--success-500'), + '--el-color-success-dark-2': getCssVariableValue('--success'), + '--el-color-success-light-3': getCssVariableValue('--success-400'), + '--el-color-success-light-5': getCssVariableValue('--success-300'), + '--el-color-success-light-7': getCssVariableValue('--success-200'), + '--el-color-success-light-8': isDark.value + ? border + : getCssVariableValue('--success-100'), + '--el-color-success-light-9': isDark.value + ? accent + : getCssVariableValue('--success-50'), + + '--el-color-warning': getCssVariableValue('--warning-500'), + '--el-color-warning-dark-2': getCssVariableValue('--warning'), + '--el-color-warning-light-3': getCssVariableValue('--warning-400'), + '--el-color-warning-light-5': getCssVariableValue('--warning-300'), + '--el-color-warning-light-7': getCssVariableValue('--warning-200'), + '--el-color-warning-light-8': isDark.value + ? border + : getCssVariableValue('--warning-100'), + '--el-color-warning-light-9': isDark.value + ? accent + : getCssVariableValue('--warning-50'), + + '--el-fill-color': getCssVariableValue('--accent'), + '--el-fill-color-blank': background, + '--el-fill-color-light': getCssVariableValue('--accent'), + '--el-fill-color-lighter': getCssVariableValue('--accent-lighter'), + + '--el-fill-color-dark': getCssVariableValue('--accent-dark'), + '--el-fill-color-darker': getCssVariableValue('--accent-darker'), + + // 解决ElLoading背景色问题 + '--el-mask-color': isDark.value + ? 'rgba(0,0,0,.8)' + : 'rgba(255,255,255,.9)', + + '--el-text-color-primary': getCssVariableValue('--foreground'), + + '--el-text-color-regular': getCssVariableValue('--foreground'), + }; + + updateCSSVariables(variables, `__vben_design_styles__`); + }, + { immediate: true }, + ); +} diff --git a/packages/effects/hooks/src/use-hover-toggle.ts b/packages/effects/hooks/src/use-hover-toggle.ts new file mode 100644 index 0000000..c9bd704 --- /dev/null +++ b/packages/effects/hooks/src/use-hover-toggle.ts @@ -0,0 +1,61 @@ +import type { Arrayable, MaybeElementRef } from '@vueuse/core'; +import type { Ref } from 'vue'; + +import { isFunction } from '@vben/utils'; +import { useMouseInElement } from '@vueuse/core'; +import { computed, onUnmounted, ref, watch } from 'vue'; + +/** + * 监测鼠标是否在元素内部,如果在元素内部则返回 true,否则返回 false + * @param refElement 所有需要检测的元素。如果提供了一个数组,那么鼠标在任何一个元素内部都会返回 true + * @param delay 延迟更新状态的时间 + * @returns 返回一个数组,第一个元素是一个 ref,表示鼠标是否在元素内部,第二个元素是一个控制器,可以通过 enable 和 disable 方法来控制监听器的启用和禁用 + */ +export function useHoverToggle( + refElement: Arrayable, + delay: (() => number) | number = 500, +) { + const isOutsides: Array> = []; + const value = ref(false); + const timer = ref | undefined>(); + const refs = Array.isArray(refElement) ? refElement : [refElement]; + refs.forEach((refEle) => { + const listener = useMouseInElement(refEle, { handleOutside: true }); + isOutsides.push(listener.isOutside); + }); + const isOutsideAll = computed(() => isOutsides.every((v) => v.value)); + + function setValueDelay(val: boolean) { + timer.value && clearTimeout(timer.value); + timer.value = setTimeout( + () => { + value.value = val; + timer.value = undefined; + }, + isFunction(delay) ? delay() : delay, + ); + } + + const watcher = watch( + isOutsideAll, + (val) => { + setValueDelay(!val); + }, + { immediate: true }, + ); + + const controller = { + enable() { + watcher.resume(); + }, + disable() { + watcher.pause(); + }, + }; + + onUnmounted(() => { + timer.value && clearTimeout(timer.value); + }); + + return [value, controller] as [typeof value, typeof controller]; +} diff --git a/packages/effects/hooks/src/use-pagination.ts b/packages/effects/hooks/src/use-pagination.ts new file mode 100644 index 0000000..8d7ee78 --- /dev/null +++ b/packages/effects/hooks/src/use-pagination.ts @@ -0,0 +1,58 @@ +import type { Ref } from 'vue'; + +import { computed, ref, unref } from 'vue'; + +/** + * Paginates an array of items + * @param list The array to paginate + * @param pageNo The current page number (1-based) + * @param pageSize Number of items per page + * @returns Paginated array slice + * @throws {Error} If pageNo or pageSize are invalid + */ +function pagination(list: T[], pageNo: number, pageSize: number): T[] { + if (pageNo < 1) throw new Error('Page number must be positive'); + if (pageSize < 1) throw new Error('Page size must be positive'); + + const offset = (pageNo - 1) * Number(pageSize); + const ret = + offset + pageSize >= list.length + ? list.slice(offset) + : list.slice(offset, offset + pageSize); + return ret; +} + +export function usePagination(list: Ref, pageSize: number) { + const currentPage = ref(1); + const pageSizeRef = ref(pageSize); + + const totalPages = computed(() => + Math.ceil(unref(list).length / unref(pageSizeRef)), + ); + + const paginationList = computed(() => { + return pagination(unref(list), unref(currentPage), unref(pageSizeRef)); + }); + + const total = computed(() => { + return unref(list).length; + }); + + function setCurrentPage(page: number) { + if (page < 1 || page > unref(totalPages)) { + throw new Error('Invalid page number'); + } + currentPage.value = page; + } + + function setPageSize(pageSize: number) { + if (pageSize < 1) { + throw new Error('Page size must be positive'); + } + pageSizeRef.value = pageSize; + // Reset to first page to prevent invalid state + currentPage.value = 1; + } + + return { setCurrentPage, total, setPageSize, paginationList }; +} diff --git a/packages/effects/hooks/src/use-refresh.ts b/packages/effects/hooks/src/use-refresh.ts new file mode 100644 index 0000000..b3a5cae --- /dev/null +++ b/packages/effects/hooks/src/use-refresh.ts @@ -0,0 +1,16 @@ +import { useRouter } from 'vue-router'; + +import { useTabbarStore } from '@vben/stores'; + +export function useRefresh() { + const router = useRouter(); + const tabbarStore = useTabbarStore(); + + async function refresh() { + await tabbarStore.refresh(router); + } + + return { + refresh, + }; +} diff --git a/packages/effects/hooks/src/use-tabs.ts b/packages/effects/hooks/src/use-tabs.ts new file mode 100644 index 0000000..2fb61c0 --- /dev/null +++ b/packages/effects/hooks/src/use-tabs.ts @@ -0,0 +1,114 @@ +import type { RouteLocationNormalized } from 'vue-router'; + +import { useTabbarStore } from '@vben/stores'; +import { useRoute, useRouter } from 'vue-router'; + +export function useTabs() { + const router = useRouter(); + const route = useRoute(); + const tabbarStore = useTabbarStore(); + + async function closeLeftTabs(tab?: RouteLocationNormalized) { + await tabbarStore.closeLeftTabs(tab || route); + } + + async function closeAllTabs() { + await tabbarStore.closeAllTabs(router); + } + + async function closeRightTabs(tab?: RouteLocationNormalized) { + await tabbarStore.closeRightTabs(tab || route); + } + + async function closeOtherTabs(tab?: RouteLocationNormalized) { + await tabbarStore.closeOtherTabs(tab || route); + } + + async function closeCurrentTab(tab?: RouteLocationNormalized) { + await tabbarStore.closeTab(tab || route, router); + } + + async function pinTab(tab?: RouteLocationNormalized) { + await tabbarStore.pinTab(tab || route); + } + + async function unpinTab(tab?: RouteLocationNormalized) { + await tabbarStore.unpinTab(tab || route); + } + + async function toggleTabPin(tab?: RouteLocationNormalized) { + await tabbarStore.toggleTabPin(tab || route); + } + + async function refreshTab() { + await tabbarStore.refresh(router); + } + + async function openTabInNewWindow(tab?: RouteLocationNormalized) { + await tabbarStore.openTabInNewWindow(tab || route); + } + + async function closeTabByKey(key: string) { + await tabbarStore.closeTabByKey(key, router); + } + + async function setTabTitle(title: string) { + tabbarStore.setUpdateTime(); + await tabbarStore.setTabTitle(route, title); + } + + async function resetTabTitle() { + tabbarStore.setUpdateTime(); + await tabbarStore.resetTabTitle(route); + } + + /** + * 获取操作是否禁用 + * @param tab + */ + function getTabDisableState(tab: RouteLocationNormalized = route) { + const tabs = tabbarStore.getTabs; + const affixTabs = tabbarStore.affixTabs; + const index = tabs.findIndex((item) => item.path === tab.path); + + const disabled = tabs.length <= 1; + + const { meta } = tab; + const affixTab = meta?.affixTab ?? false; + const isCurrentTab = route.path === tab.path; + + // 当前处于最左侧或者减去固定标签页的数量等于0 + const disabledCloseLeft = + index === 0 || index - affixTabs.length <= 0 || !isCurrentTab; + + const disabledCloseRight = !isCurrentTab || index === tabs.length - 1; + + const disabledCloseOther = + disabled || !isCurrentTab || tabs.length - affixTabs.length <= 1; + return { + disabledCloseAll: disabled, + disabledCloseCurrent: !!affixTab || disabled, + disabledCloseLeft, + disabledCloseOther, + disabledCloseRight, + disabledRefresh: !isCurrentTab, + }; + } + + return { + closeAllTabs, + closeCurrentTab, + closeLeftTabs, + closeOtherTabs, + closeRightTabs, + closeTabByKey, + getTabDisableState, + openTabInNewWindow, + pinTab, + refreshTab, + resetTabTitle, + setTabTitle, + toggleTabPin, + unpinTab, + }; +} diff --git a/packages/effects/hooks/src/use-watermark.ts b/packages/effects/hooks/src/use-watermark.ts new file mode 100644 index 0000000..c3f56ea --- /dev/null +++ b/packages/effects/hooks/src/use-watermark.ts @@ -0,0 +1,88 @@ +import type { Watermark, WatermarkOptions } from 'watermark-js-plus'; + +import { nextTick, onUnmounted, readonly, ref } from 'vue'; + +import { updatePreferences } from '@vben/preferences'; + +const watermark = ref(); +const unmountedHooked = ref(false); +const cachedOptions = ref>({ + advancedStyle: { + colorStops: [ + { + color: 'gray', + offset: 0, + }, + { + color: 'gray', + offset: 1, + }, + ], + type: 'linear', + }, + // fontSize: '20px', + content: '', + contentType: 'multi-line-text', + globalAlpha: 0.25, + gridLayoutOptions: { + cols: 2, + gap: [20, 20], + matrix: [ + [1, 0], + [0, 1], + ], + rows: 2, + }, + height: 200, + layout: 'grid', + rotate: 30, + width: 160, +}); + +export function useWatermark() { + async function initWatermark(options: Partial) { + const { Watermark } = await import('watermark-js-plus'); + + cachedOptions.value = { + ...cachedOptions.value, + ...options, + }; + watermark.value = new Watermark(cachedOptions.value); + updatePreferences({ app: { watermark: true } }); + await watermark.value?.create(); + } + + async function updateWatermark(options: Partial) { + if (watermark.value) { + await nextTick(); + await watermark.value?.changeOptions({ + ...cachedOptions.value, + ...options, + }); + } else { + await initWatermark(options); + } + } + + function destroyWatermark() { + if (watermark.value) { + watermark.value.destroy(); + watermark.value = undefined; + } + updatePreferences({ app: { watermark: false } }); + } + + // 只在第一次调用时注册卸载钩子,防止重复注册以致于在路由切换时销毁了水印 + if (!unmountedHooked.value) { + unmountedHooked.value = true; + onUnmounted(() => { + destroyWatermark(); + }); + } + + return { + destroyWatermark, + updateWatermark, + watermark: readonly(watermark), + }; +} diff --git a/packages/effects/hooks/tsconfig.json b/packages/effects/hooks/tsconfig.json new file mode 100644 index 0000000..b13aa7a --- /dev/null +++ b/packages/effects/hooks/tsconfig.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "compilerOptions": { + "types": ["vite/client", "@vben/types/global"] + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/effects/layouts/package.json b/packages/effects/layouts/package.json new file mode 100644 index 0000000..34da7f9 --- /dev/null +++ b/packages/effects/layouts/package.json @@ -0,0 +1,43 @@ +{ + "name": "@vben/layouts", + "version": "5.5.3", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/effects/layouts" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben-core/composables": "workspace:*", + "@vben-core/form-ui": "workspace:*", + "@vben-core/layout-ui": "workspace:*", + "@vben-core/menu-ui": "workspace:*", + "@vben-core/popup-ui": "workspace:*", + "@vben-core/shadcn-ui": "workspace:*", + "@vben-core/shared": "workspace:*", + "@vben-core/tabs-ui": "workspace:*", + "@vben/constants": "workspace:*", + "@vben/hooks": "workspace:*", + "@vben/icons": "workspace:*", + "@vben/locales": "workspace:*", + "@vben/preferences": "workspace:*", + "@vben/stores": "workspace:*", + "@vben/types": "workspace:*", + "@vben/utils": "workspace:*", + "@vueuse/core": "catalog:", + "vue": "catalog:", + "vue-router": "catalog:" + } +} diff --git a/packages/effects/layouts/src/authentication/authentication copy.vue b/packages/effects/layouts/src/authentication/authentication copy.vue new file mode 100644 index 0000000..ceae802 --- /dev/null +++ b/packages/effects/layouts/src/authentication/authentication copy.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/packages/effects/layouts/src/authentication/authentication.vue b/packages/effects/layouts/src/authentication/authentication.vue new file mode 100644 index 0000000..d8362ed --- /dev/null +++ b/packages/effects/layouts/src/authentication/authentication.vue @@ -0,0 +1,189 @@ + + + + + + diff --git a/packages/effects/layouts/src/authentication/bg.png b/packages/effects/layouts/src/authentication/bg.png new file mode 100644 index 0000000..a97f1df Binary files /dev/null and b/packages/effects/layouts/src/authentication/bg.png differ diff --git a/packages/effects/layouts/src/authentication/form.vue b/packages/effects/layouts/src/authentication/form.vue new file mode 100644 index 0000000..80c9074 --- /dev/null +++ b/packages/effects/layouts/src/authentication/form.vue @@ -0,0 +1,33 @@ + + + diff --git a/packages/effects/layouts/src/authentication/icons/slogan.vue b/packages/effects/layouts/src/authentication/icons/slogan.vue new file mode 100644 index 0000000..82cc119 --- /dev/null +++ b/packages/effects/layouts/src/authentication/icons/slogan.vue @@ -0,0 +1,4568 @@ + diff --git a/packages/effects/layouts/src/authentication/index.ts b/packages/effects/layouts/src/authentication/index.ts new file mode 100644 index 0000000..6c684d1 --- /dev/null +++ b/packages/effects/layouts/src/authentication/index.ts @@ -0,0 +1 @@ +export { default as AuthPageLayout } from './authentication.vue'; diff --git a/packages/effects/layouts/src/authentication/logo.png b/packages/effects/layouts/src/authentication/logo.png new file mode 100644 index 0000000..b27828f Binary files /dev/null and b/packages/effects/layouts/src/authentication/logo.png differ diff --git a/packages/effects/layouts/src/authentication/toolbar.vue b/packages/effects/layouts/src/authentication/toolbar.vue new file mode 100644 index 0000000..94d321a --- /dev/null +++ b/packages/effects/layouts/src/authentication/toolbar.vue @@ -0,0 +1,49 @@ + + + diff --git a/packages/effects/layouts/src/authentication/types.ts b/packages/effects/layouts/src/authentication/types.ts new file mode 100644 index 0000000..c4c1c7f --- /dev/null +++ b/packages/effects/layouts/src/authentication/types.ts @@ -0,0 +1 @@ +export type ToolbarType = 'color' | 'language' | 'layout' | 'theme'; diff --git a/packages/effects/layouts/src/basic/README.md b/packages/effects/layouts/src/basic/README.md new file mode 100644 index 0000000..b1266ea --- /dev/null +++ b/packages/effects/layouts/src/basic/README.md @@ -0,0 +1,7 @@ +## layout + +### header + +- 支持N个自定义插槽,命名方式:header-right-n,header-left-n +- header-left-n ,排序方式:0-19 ,breadcrumb 21-x +- header-right-n ,排序方式:0-49,global-search,51-59,theme-toggle,61-69,language-toggle,71-79,fullscreen,81-89,notification,91-149,user-dropdown,151-x diff --git a/packages/effects/layouts/src/basic/content/content-spinner.vue b/packages/effects/layouts/src/basic/content/content-spinner.vue new file mode 100644 index 0000000..e97e4b6 --- /dev/null +++ b/packages/effects/layouts/src/basic/content/content-spinner.vue @@ -0,0 +1,12 @@ + + diff --git a/packages/effects/layouts/src/basic/content/content.vue b/packages/effects/layouts/src/basic/content/content.vue new file mode 100644 index 0000000..5188bdc --- /dev/null +++ b/packages/effects/layouts/src/basic/content/content.vue @@ -0,0 +1,113 @@ + + + diff --git a/packages/effects/layouts/src/basic/content/index.ts b/packages/effects/layouts/src/basic/content/index.ts new file mode 100644 index 0000000..500cbcc --- /dev/null +++ b/packages/effects/layouts/src/basic/content/index.ts @@ -0,0 +1,2 @@ +export { default as LayoutContent } from './content.vue'; +export { default as LayoutContentSpinner } from './content-spinner.vue'; diff --git a/packages/effects/layouts/src/basic/content/use-content-spinner.ts b/packages/effects/layouts/src/basic/content/use-content-spinner.ts new file mode 100644 index 0000000..dfe3c53 --- /dev/null +++ b/packages/effects/layouts/src/basic/content/use-content-spinner.ts @@ -0,0 +1,50 @@ +import { computed, ref } from 'vue'; +import { useRouter } from 'vue-router'; + +import { preferences } from '@vben/preferences'; + +function useContentSpinner() { + const spinning = ref(false); + const startTime = ref(0); + const router = useRouter(); + const minShowTime = 500; // 最小显示时间 + const enableLoading = computed(() => preferences.transition.loading); + + // 结束加载动画 + const onEnd = () => { + if (!enableLoading.value) { + return; + } + const processTime = performance.now() - startTime.value; + if (processTime < minShowTime) { + setTimeout(() => { + spinning.value = false; + }, minShowTime - processTime); + } else { + spinning.value = false; + } + }; + + // 路由前置守卫 + router.beforeEach((to) => { + if (to.meta.loaded || !enableLoading.value || to.meta.iframeSrc) { + return true; + } + startTime.value = performance.now(); + spinning.value = true; + return true; + }); + + // 路由后置守卫 + router.afterEach((to) => { + if (to.meta.loaded || !enableLoading.value || to.meta.iframeSrc) { + return true; + } + onEnd(); + return true; + }); + + return { spinning }; +} + +export { useContentSpinner }; diff --git a/packages/effects/layouts/src/basic/copyright/copyright.vue b/packages/effects/layouts/src/basic/copyright/copyright.vue new file mode 100644 index 0000000..6a214d1 --- /dev/null +++ b/packages/effects/layouts/src/basic/copyright/copyright.vue @@ -0,0 +1,46 @@ + + + diff --git a/packages/effects/layouts/src/basic/copyright/index.ts b/packages/effects/layouts/src/basic/copyright/index.ts new file mode 100644 index 0000000..e0620da --- /dev/null +++ b/packages/effects/layouts/src/basic/copyright/index.ts @@ -0,0 +1 @@ +export { default as Copyright } from './copyright.vue'; diff --git a/packages/effects/layouts/src/basic/footer/footer.vue b/packages/effects/layouts/src/basic/footer/footer.vue new file mode 100644 index 0000000..6fc256d --- /dev/null +++ b/packages/effects/layouts/src/basic/footer/footer.vue @@ -0,0 +1,11 @@ + + + diff --git a/packages/effects/layouts/src/basic/footer/index.ts b/packages/effects/layouts/src/basic/footer/index.ts new file mode 100644 index 0000000..7e149a2 --- /dev/null +++ b/packages/effects/layouts/src/basic/footer/index.ts @@ -0,0 +1 @@ +export { default as LayoutFooter } from './footer.vue'; diff --git a/packages/effects/layouts/src/basic/header/header.vue b/packages/effects/layouts/src/basic/header/header.vue new file mode 100644 index 0000000..9738746 --- /dev/null +++ b/packages/effects/layouts/src/basic/header/header.vue @@ -0,0 +1,183 @@ + + + + diff --git a/packages/effects/layouts/src/basic/header/index.ts b/packages/effects/layouts/src/basic/header/index.ts new file mode 100644 index 0000000..44f0729 --- /dev/null +++ b/packages/effects/layouts/src/basic/header/index.ts @@ -0,0 +1 @@ +export { default as LayoutHeader } from './header.vue'; diff --git a/packages/effects/layouts/src/basic/index.ts b/packages/effects/layouts/src/basic/index.ts new file mode 100644 index 0000000..b3a01cd --- /dev/null +++ b/packages/effects/layouts/src/basic/index.ts @@ -0,0 +1 @@ +export { default as BasicLayout } from './layout.vue'; diff --git a/packages/effects/layouts/src/basic/layout.vue b/packages/effects/layouts/src/basic/layout.vue new file mode 100644 index 0000000..1fb9044 --- /dev/null +++ b/packages/effects/layouts/src/basic/layout.vue @@ -0,0 +1,356 @@ + + + diff --git a/packages/effects/layouts/src/basic/menu/extra-menu.vue b/packages/effects/layouts/src/basic/menu/extra-menu.vue new file mode 100644 index 0000000..bc2d0f7 --- /dev/null +++ b/packages/effects/layouts/src/basic/menu/extra-menu.vue @@ -0,0 +1,39 @@ + + + diff --git a/packages/effects/layouts/src/basic/menu/index.ts b/packages/effects/layouts/src/basic/menu/index.ts new file mode 100644 index 0000000..72b87ad --- /dev/null +++ b/packages/effects/layouts/src/basic/menu/index.ts @@ -0,0 +1,5 @@ +export { default as LayoutExtraMenu } from './extra-menu.vue'; +export { default as LayoutMenu } from './menu.vue'; +export { default as LayoutMixedMenu } from './mixed-menu.vue'; +export * from './use-extra-menu'; +export * from './use-mixed-menu'; diff --git a/packages/effects/layouts/src/basic/menu/menu.vue b/packages/effects/layouts/src/basic/menu/menu.vue new file mode 100644 index 0000000..812813a --- /dev/null +++ b/packages/effects/layouts/src/basic/menu/menu.vue @@ -0,0 +1,43 @@ + + + diff --git a/packages/effects/layouts/src/basic/menu/mixed-menu.vue b/packages/effects/layouts/src/basic/menu/mixed-menu.vue new file mode 100644 index 0000000..7f5f006 --- /dev/null +++ b/packages/effects/layouts/src/basic/menu/mixed-menu.vue @@ -0,0 +1,43 @@ + + + diff --git a/packages/effects/layouts/src/basic/menu/use-extra-menu.ts b/packages/effects/layouts/src/basic/menu/use-extra-menu.ts new file mode 100644 index 0000000..2383e39 --- /dev/null +++ b/packages/effects/layouts/src/basic/menu/use-extra-menu.ts @@ -0,0 +1,127 @@ +import type { MenuRecordRaw } from '@vben/types'; +import type { ComputedRef } from 'vue'; + +import { preferences } from '@vben/preferences'; +import { useAccessStore } from '@vben/stores'; +import { findRootMenuByPath } from '@vben/utils'; +import { computed, ref, watch } from 'vue'; +import { useRoute } from 'vue-router'; + +import { useNavigation } from './use-navigation'; + +function useExtraMenu(useRootMenus?: ComputedRef) { + const accessStore = useAccessStore(); + const { navigation } = useNavigation(); + + const menus = computed(() => useRootMenus?.value ?? accessStore.accessMenus); + + /** 记录当前顶级菜单下哪个子菜单最后激活 */ + const defaultSubMap = new Map(); + const extraRootMenus = ref([]); + const route = useRoute(); + const extraMenus = ref([]); + const sidebarExtraVisible = ref(false); + const extraActiveMenu = ref(''); + const parentLevel = computed(() => + preferences.app.layout === 'header-mixed-nav' ? 1 : 0, + ); + + /** + * 选择混合菜单事件 + * @param menu + */ + const handleMixedMenuSelect = async (menu: MenuRecordRaw) => { + extraMenus.value = menu?.children ?? []; + extraActiveMenu.value = menu.parents?.[parentLevel.value] ?? menu.path; + const hasChildren = extraMenus.value.length > 0; + + sidebarExtraVisible.value = hasChildren; + if (!hasChildren) { + await navigation(menu.path); + } else if (preferences.sidebar.autoActivateChild) { + await navigation( + defaultSubMap.has(menu.path) + ? (defaultSubMap.get(menu.path) as string) + : menu.path, + ); + } + }; + + /** + * 选择默认菜单事件 + * @param menu + * @param rootMenu + */ + const handleDefaultSelect = async ( + menu: MenuRecordRaw, + rootMenu?: MenuRecordRaw, + ) => { + extraMenus.value = rootMenu?.children ?? extraRootMenus.value ?? []; + extraActiveMenu.value = menu.parents?.[parentLevel.value] ?? menu.path; + + if (preferences.sidebar.expandOnHover) { + sidebarExtraVisible.value = extraMenus.value.length > 0; + } + }; + + /** + * 侧边菜单鼠标移出事件 + */ + const handleSideMouseLeave = () => { + if (preferences.sidebar.expandOnHover) { + return; + } + + const { findMenu, rootMenu, rootMenuPath } = findRootMenuByPath( + menus.value, + route.path, + ); + extraActiveMenu.value = rootMenuPath ?? findMenu?.path ?? ''; + extraMenus.value = rootMenu?.children ?? []; + }; + + const handleMenuMouseEnter = (menu: MenuRecordRaw) => { + if (!preferences.sidebar.expandOnHover) { + const { findMenu } = findRootMenuByPath(menus.value, menu.path); + extraMenus.value = findMenu?.children ?? []; + extraActiveMenu.value = menu.parents?.[parentLevel.value] ?? menu.path; + sidebarExtraVisible.value = extraMenus.value.length > 0; + } + }; + + function calcExtraMenus(path: string) { + const currentPath = route.meta?.activePath || path; + const { findMenu, rootMenu, rootMenuPath } = findRootMenuByPath( + menus.value, + currentPath, + parentLevel.value, + ); + extraRootMenus.value = rootMenu?.children ?? []; + if (rootMenuPath) defaultSubMap.set(rootMenuPath, currentPath); + extraActiveMenu.value = rootMenuPath ?? findMenu?.path ?? ''; + extraMenus.value = rootMenu?.children ?? []; + if (preferences.sidebar.expandOnHover) { + sidebarExtraVisible.value = extraMenus.value.length > 0; + } + } + + watch( + () => [route.path, preferences.app.layout], + ([path]) => { + calcExtraMenus(path || ''); + }, + { immediate: true }, + ); + + return { + extraActiveMenu, + extraMenus, + handleDefaultSelect, + handleMenuMouseEnter, + handleMixedMenuSelect, + handleSideMouseLeave, + sidebarExtraVisible, + }; +} + +export { useExtraMenu }; diff --git a/packages/effects/layouts/src/basic/menu/use-mixed-menu.ts b/packages/effects/layouts/src/basic/menu/use-mixed-menu.ts new file mode 100644 index 0000000..c1d27fd --- /dev/null +++ b/packages/effects/layouts/src/basic/menu/use-mixed-menu.ts @@ -0,0 +1,165 @@ +import type { MenuRecordRaw } from '@vben/types'; + +import { computed, onBeforeMount, ref, watch } from 'vue'; +import { useRoute } from 'vue-router'; + +import { preferences, usePreferences } from '@vben/preferences'; +import { useAccessStore } from '@vben/stores'; +import { findRootMenuByPath } from '@vben/utils'; + +import { useNavigation } from './use-navigation'; + +function useMixedMenu() { + const { navigation } = useNavigation(); + const accessStore = useAccessStore(); + const route = useRoute(); + const splitSideMenus = ref([]); + const rootMenuPath = ref(''); + const mixedRootMenuPath = ref(''); + const mixExtraMenus = ref([]); + /** 记录当前顶级菜单下哪个子菜单最后激活 */ + const defaultSubMap = new Map(); + const { isMixedNav, isHeaderMixedNav } = usePreferences(); + + const needSplit = computed( + () => + (preferences.navigation.split && isMixedNav.value) || + isHeaderMixedNav.value, + ); + + const sidebarVisible = computed(() => { + const enableSidebar = preferences.sidebar.enable; + if (needSplit.value) { + return enableSidebar && splitSideMenus.value.length > 0; + } + return enableSidebar; + }); + const menus = computed(() => accessStore.accessMenus); + + /** + * 头部菜单 + */ + const headerMenus = computed(() => { + if (!needSplit.value) { + return menus.value; + } + return menus.value.map((item) => { + return { + ...item, + children: [], + }; + }); + }); + + /** + * 侧边菜单 + */ + const sidebarMenus = computed(() => { + return needSplit.value ? splitSideMenus.value : menus.value; + }); + + const mixHeaderMenus = computed(() => { + return isHeaderMixedNav.value ? sidebarMenus.value : headerMenus.value; + }); + + /** + * 侧边菜单激活路径 + */ + const sidebarActive = computed(() => { + return (route?.meta?.activePath as string) ?? route.path; + }); + + /** + * 头部菜单激活路径 + */ + const headerActive = computed(() => { + if (!needSplit.value) { + return route.path; + } + return rootMenuPath.value; + }); + + /** + * 菜单点击事件处理 + * @param key 菜单路径 + * @param mode 菜单模式 + */ + const handleMenuSelect = (key: string, mode?: string) => { + if (!needSplit.value || mode === 'vertical') { + navigation(key); + return; + } + + const rootMenu = menus.value.find((item) => item.path === key); + rootMenuPath.value = rootMenu?.path ?? ''; + splitSideMenus.value = rootMenu?.children ?? []; + if (splitSideMenus.value.length === 0) { + navigation(key); + } else if (rootMenu && preferences.sidebar.autoActivateChild) { + navigation( + defaultSubMap.has(rootMenu.path) + ? (defaultSubMap.get(rootMenu.path) as string) + : rootMenu.path, + ); + } + }; + + /** + * 侧边菜单展开事件 + * @param key 路由路径 + * @param parentsPath 父级路径 + */ + const handleMenuOpen = (key: string, parentsPath: string[]) => { + if (parentsPath.length <= 1 && preferences.sidebar.autoActivateChild) { + navigation( + defaultSubMap.has(key) ? (defaultSubMap.get(key) as string) : key, + ); + } + }; + + /** + * 计算侧边菜单 + * @param path 路由路径 + */ + function calcSideMenus(path: string = route.path) { + let { rootMenu } = findRootMenuByPath(menus.value, path); + if (!rootMenu) { + rootMenu = menus.value.find((item) => item.path === path); + } + const result = findRootMenuByPath(rootMenu?.children || [], path, 1); + mixedRootMenuPath.value = result.rootMenuPath ?? ''; + mixExtraMenus.value = result.rootMenu?.children ?? []; + rootMenuPath.value = rootMenu?.path ?? ''; + splitSideMenus.value = rootMenu?.children ?? []; + } + + watch( + () => route.path, + (path) => { + const currentPath = (route?.meta?.activePath as string) ?? path; + calcSideMenus(currentPath); + if (rootMenuPath.value) + defaultSubMap.set(rootMenuPath.value, currentPath); + }, + { immediate: true }, + ); + + // 初始化计算侧边菜单 + onBeforeMount(() => { + calcSideMenus(route.meta?.activePath || route.path); + }); + + return { + handleMenuSelect, + handleMenuOpen, + headerActive, + headerMenus, + sidebarActive, + sidebarMenus, + mixHeaderMenus, + mixExtraMenus, + sidebarVisible, + }; +} + +export { useMixedMenu }; diff --git a/packages/effects/layouts/src/basic/menu/use-navigation.ts b/packages/effects/layouts/src/basic/menu/use-navigation.ts new file mode 100644 index 0000000..4c240a8 --- /dev/null +++ b/packages/effects/layouts/src/basic/menu/use-navigation.ts @@ -0,0 +1,34 @@ +import type { RouteRecordNormalized } from 'vue-router'; + +import { isHttpUrl, openRouteInNewWindow, openWindow } from '@vben/utils'; +import { useRouter } from 'vue-router'; + +function useNavigation() { + const router = useRouter(); + const routes = router.getRoutes(); + + const routeMetaMap = new Map(); + + routes.forEach((route) => { + routeMetaMap.set(route.path, route); + }); + + const navigation = async (path: string) => { + const route = routeMetaMap.get(path); + const { openInNewWindow = false, query = {} } = route?.meta ?? {}; + if (isHttpUrl(path)) { + openWindow(path, { target: '_blank' }); + } else if (openInNewWindow) { + openRouteInNewWindow(path); + } else { + await router.push({ + path, + query, + }); + } + }; + + return { navigation }; +} + +export { useNavigation }; diff --git a/packages/effects/layouts/src/basic/tabbar/index.ts b/packages/effects/layouts/src/basic/tabbar/index.ts new file mode 100644 index 0000000..5cc2479 --- /dev/null +++ b/packages/effects/layouts/src/basic/tabbar/index.ts @@ -0,0 +1,2 @@ +export { default as LayoutTabbar } from './tabbar.vue'; +export * from './use-tabbar'; diff --git a/packages/effects/layouts/src/basic/tabbar/tabbar.vue b/packages/effects/layouts/src/basic/tabbar/tabbar.vue new file mode 100644 index 0000000..b67b3a4 --- /dev/null +++ b/packages/effects/layouts/src/basic/tabbar/tabbar.vue @@ -0,0 +1,75 @@ + + + diff --git a/packages/effects/layouts/src/basic/tabbar/use-tabbar.ts b/packages/effects/layouts/src/basic/tabbar/use-tabbar.ts new file mode 100644 index 0000000..2ebb35c --- /dev/null +++ b/packages/effects/layouts/src/basic/tabbar/use-tabbar.ts @@ -0,0 +1,219 @@ +import type { TabDefinition } from '@vben/types'; +import type { IContextMenuItem } from '@vben-core/tabs-ui'; +import type { RouteLocationNormalizedGeneric } from 'vue-router'; + +import { useContentMaximize, useTabs } from '@vben/hooks'; +import { + ArrowLeftToLine, + ArrowRightLeft, + ArrowRightToLine, + ExternalLink, + FoldHorizontal, + Fullscreen, + Minimize2, + Pin, + PinOff, + RotateCw, + X, +} from '@vben/icons'; +import { $t, useI18n } from '@vben/locales'; +import { useAccessStore, useTabbarStore } from '@vben/stores'; +import { filterTree } from '@vben/utils'; +import { computed, ref, watch } from 'vue'; +import { useRoute, useRouter } from 'vue-router'; + +export function useTabbar() { + const router = useRouter(); + const route = useRoute(); + const accessStore = useAccessStore(); + const tabbarStore = useTabbarStore(); + const { contentIsMaximize, toggleMaximize } = useContentMaximize(); + const { + closeAllTabs, + closeCurrentTab, + closeLeftTabs, + closeOtherTabs, + closeRightTabs, + closeTabByKey, + getTabDisableState, + openTabInNewWindow, + refreshTab, + toggleTabPin, + } = useTabs(); + + const currentActive = computed(() => { + return route.fullPath; + }); + + const { locale } = useI18n(); + const currentTabs = ref(); + watch( + [ + () => tabbarStore.getTabs, + () => tabbarStore.updateTime, + () => locale.value, + ], + ([tabs]) => { + currentTabs.value = tabs.map((item) => wrapperTabLocale(item)); + }, + ); + + /** + * 初始化固定标签页 + */ + const initAffixTabs = () => { + const affixTabs = filterTree(router.getRoutes(), (route) => { + return !!route.meta?.affixTab; + }); + tabbarStore.setAffixTabs(affixTabs); + }; + + // 点击tab,跳转路由 + const handleClick = (key: string) => { + router.push(key); + }; + + // 关闭tab + const handleClose = async (key: string) => { + await closeTabByKey(key); + }; + + function wrapperTabLocale(tab: RouteLocationNormalizedGeneric) { + return { + ...tab, + meta: { + ...tab?.meta, + title: $t(tab?.meta?.title as string), + }, + }; + } + + watch( + () => accessStore.accessMenus, + () => { + initAffixTabs(); + }, + { immediate: true }, + ); + + watch( + () => route.path, + () => { + const meta = route.matched?.[route.matched.length - 1]?.meta; + tabbarStore.addTab({ + ...route, + meta: meta || route.meta, + }); + }, + { immediate: true }, + ); + + const createContextMenus = (tab: TabDefinition) => { + const { + disabledCloseAll, + disabledCloseCurrent, + disabledCloseLeft, + disabledCloseOther, + disabledCloseRight, + disabledRefresh, + } = getTabDisableState(tab); + + const affixTab = tab?.meta?.affixTab ?? false; + + const menus: IContextMenuItem[] = [ + { + disabled: disabledCloseCurrent, + handler: async () => { + await closeCurrentTab(tab); + }, + icon: X, + key: 'close', + text: $t('preferences.tabbar.contextMenu.close'), + }, + { + handler: async () => { + await toggleTabPin(tab); + }, + icon: affixTab ? PinOff : Pin, + key: 'affix', + text: affixTab + ? $t('preferences.tabbar.contextMenu.unpin') + : $t('preferences.tabbar.contextMenu.pin'), + }, + { + handler: async () => { + if (!contentIsMaximize.value) { + await router.push(tab.fullPath); + } + toggleMaximize(); + }, + icon: contentIsMaximize.value ? Minimize2 : Fullscreen, + key: contentIsMaximize.value ? 'restore-maximize' : 'maximize', + text: contentIsMaximize.value + ? $t('preferences.tabbar.contextMenu.restoreMaximize') + : $t('preferences.tabbar.contextMenu.maximize'), + }, + { + disabled: disabledRefresh, + handler: refreshTab, + icon: RotateCw, + key: 'reload', + text: $t('preferences.tabbar.contextMenu.reload'), + }, + { + handler: async () => { + await openTabInNewWindow(tab); + }, + icon: ExternalLink, + key: 'open-in-new-window', + separator: true, + text: $t('preferences.tabbar.contextMenu.openInNewWindow'), + }, + + { + disabled: disabledCloseLeft, + handler: async () => { + await closeLeftTabs(tab); + }, + icon: ArrowLeftToLine, + key: 'close-left', + text: $t('preferences.tabbar.contextMenu.closeLeft'), + }, + { + disabled: disabledCloseRight, + handler: async () => { + await closeRightTabs(tab); + }, + icon: ArrowRightToLine, + key: 'close-right', + separator: true, + text: $t('preferences.tabbar.contextMenu.closeRight'), + }, + { + disabled: disabledCloseOther, + handler: async () => { + await closeOtherTabs(tab); + }, + icon: FoldHorizontal, + key: 'close-other', + text: $t('preferences.tabbar.contextMenu.closeOther'), + }, + { + disabled: disabledCloseAll, + handler: closeAllTabs, + icon: ArrowRightLeft, + key: 'close-all', + text: $t('preferences.tabbar.contextMenu.closeAll'), + }, + ]; + return menus; + }; + + return { + createContextMenus, + currentActive, + currentTabs, + handleClick, + handleClose, + }; +} diff --git a/packages/effects/layouts/src/iframe/iframe-router-view.vue b/packages/effects/layouts/src/iframe/iframe-router-view.vue new file mode 100644 index 0000000..dcfba5b --- /dev/null +++ b/packages/effects/layouts/src/iframe/iframe-router-view.vue @@ -0,0 +1,84 @@ + + diff --git a/packages/effects/layouts/src/iframe/iframe-view.vue b/packages/effects/layouts/src/iframe/iframe-view.vue new file mode 100644 index 0000000..7b8b46c --- /dev/null +++ b/packages/effects/layouts/src/iframe/iframe-view.vue @@ -0,0 +1,3 @@ + diff --git a/packages/effects/layouts/src/iframe/index.ts b/packages/effects/layouts/src/iframe/index.ts new file mode 100644 index 0000000..1b8c131 --- /dev/null +++ b/packages/effects/layouts/src/iframe/index.ts @@ -0,0 +1,2 @@ +export { default as IFrameRouterView } from './iframe-router-view.vue'; +export { default as IFrameView } from './iframe-view.vue'; diff --git a/packages/effects/layouts/src/index.ts b/packages/effects/layouts/src/index.ts new file mode 100644 index 0000000..124a44a --- /dev/null +++ b/packages/effects/layouts/src/index.ts @@ -0,0 +1,4 @@ +export * from './authentication'; +export * from './basic'; +export * from './iframe'; +export * from './widgets'; diff --git a/packages/effects/layouts/src/widgets/breadcrumb.vue b/packages/effects/layouts/src/widgets/breadcrumb.vue new file mode 100644 index 0000000..a2402d2 --- /dev/null +++ b/packages/effects/layouts/src/widgets/breadcrumb.vue @@ -0,0 +1,71 @@ + + diff --git a/packages/effects/layouts/src/widgets/check-updates/check-updates.vue b/packages/effects/layouts/src/widgets/check-updates/check-updates.vue new file mode 100644 index 0000000..f87075c --- /dev/null +++ b/packages/effects/layouts/src/widgets/check-updates/check-updates.vue @@ -0,0 +1,133 @@ + + diff --git a/packages/effects/layouts/src/widgets/check-updates/index.ts b/packages/effects/layouts/src/widgets/check-updates/index.ts new file mode 100644 index 0000000..fc20c66 --- /dev/null +++ b/packages/effects/layouts/src/widgets/check-updates/index.ts @@ -0,0 +1 @@ +export { default as CheckUpdates } from './check-updates.vue'; diff --git a/packages/effects/layouts/src/widgets/color-toggle.vue b/packages/effects/layouts/src/widgets/color-toggle.vue new file mode 100644 index 0000000..67cad31 --- /dev/null +++ b/packages/effects/layouts/src/widgets/color-toggle.vue @@ -0,0 +1,63 @@ + + + diff --git a/packages/effects/layouts/src/widgets/global-search/global-search.vue b/packages/effects/layouts/src/widgets/global-search/global-search.vue new file mode 100644 index 0000000..7f5d54a --- /dev/null +++ b/packages/effects/layouts/src/widgets/global-search/global-search.vue @@ -0,0 +1,154 @@ + + + diff --git a/packages/effects/layouts/src/widgets/global-search/index.ts b/packages/effects/layouts/src/widgets/global-search/index.ts new file mode 100644 index 0000000..cd490ae --- /dev/null +++ b/packages/effects/layouts/src/widgets/global-search/index.ts @@ -0,0 +1 @@ +export { default as GlobalSearch } from './global-search.vue'; diff --git a/packages/effects/layouts/src/widgets/global-search/search-panel.vue b/packages/effects/layouts/src/widgets/global-search/search-panel.vue new file mode 100644 index 0000000..aa40e34 --- /dev/null +++ b/packages/effects/layouts/src/widgets/global-search/search-panel.vue @@ -0,0 +1,285 @@ + + + diff --git a/packages/effects/layouts/src/widgets/index.ts b/packages/effects/layouts/src/widgets/index.ts new file mode 100644 index 0000000..f6a4a7b --- /dev/null +++ b/packages/effects/layouts/src/widgets/index.ts @@ -0,0 +1,11 @@ +export { default as Breadcrumb } from './breadcrumb.vue'; +export * from './check-updates'; +export { default as AuthenticationColorToggle } from './color-toggle.vue'; +export * from './global-search'; +export { default as LanguageToggle } from './language-toggle.vue'; +export { default as AuthenticationLayoutToggle } from './layout-toggle.vue'; +export * from './lock-screen'; +export * from './notification'; +export * from './preferences'; +export * from './theme-toggle'; +export * from './user-dropdown'; diff --git a/packages/effects/layouts/src/widgets/language-toggle.vue b/packages/effects/layouts/src/widgets/language-toggle.vue new file mode 100644 index 0000000..c8b612b --- /dev/null +++ b/packages/effects/layouts/src/widgets/language-toggle.vue @@ -0,0 +1,37 @@ + + + diff --git a/packages/effects/layouts/src/widgets/layout-toggle.vue b/packages/effects/layouts/src/widgets/layout-toggle.vue new file mode 100644 index 0000000..edfadbd --- /dev/null +++ b/packages/effects/layouts/src/widgets/layout-toggle.vue @@ -0,0 +1,60 @@ + + + diff --git a/packages/effects/layouts/src/widgets/lock-screen/index.ts b/packages/effects/layouts/src/widgets/lock-screen/index.ts new file mode 100644 index 0000000..1609bec --- /dev/null +++ b/packages/effects/layouts/src/widgets/lock-screen/index.ts @@ -0,0 +1,2 @@ +export { default as LockScreen } from './lock-screen.vue'; +export { default as LockScreenModal } from './lock-screen-modal.vue'; diff --git a/packages/effects/layouts/src/widgets/lock-screen/lock-screen-modal.vue b/packages/effects/layouts/src/widgets/lock-screen/lock-screen-modal.vue new file mode 100644 index 0000000..3fc4399 --- /dev/null +++ b/packages/effects/layouts/src/widgets/lock-screen/lock-screen-modal.vue @@ -0,0 +1,100 @@ + + + diff --git a/packages/effects/layouts/src/widgets/lock-screen/lock-screen.vue b/packages/effects/layouts/src/widgets/lock-screen/lock-screen.vue new file mode 100644 index 0000000..66b39da --- /dev/null +++ b/packages/effects/layouts/src/widgets/lock-screen/lock-screen.vue @@ -0,0 +1,153 @@ + + + diff --git a/packages/effects/layouts/src/widgets/notification/index.ts b/packages/effects/layouts/src/widgets/notification/index.ts new file mode 100644 index 0000000..e219b71 --- /dev/null +++ b/packages/effects/layouts/src/widgets/notification/index.ts @@ -0,0 +1,3 @@ +export { default as Notification } from './notification.vue'; + +export type * from './types'; diff --git a/packages/effects/layouts/src/widgets/notification/notification.vue b/packages/effects/layouts/src/widgets/notification/notification.vue new file mode 100644 index 0000000..862ee74 --- /dev/null +++ b/packages/effects/layouts/src/widgets/notification/notification.vue @@ -0,0 +1,205 @@ + + + + + + diff --git a/packages/effects/layouts/src/widgets/notification/types.ts b/packages/effects/layouts/src/widgets/notification/types.ts new file mode 100644 index 0000000..a09d8ed --- /dev/null +++ b/packages/effects/layouts/src/widgets/notification/types.ts @@ -0,0 +1,11 @@ +interface NotificationItem { + avatar: string; + date: string; + isRead?: boolean; + message: string; + title: string; + userId: number | string; + type?: string|number; +} + +export type { NotificationItem }; diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/block.vue b/packages/effects/layouts/src/widgets/preferences/blocks/block.vue new file mode 100644 index 0000000..c8cc9e6 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/block.vue @@ -0,0 +1,22 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/general/animation.vue b/packages/effects/layouts/src/widgets/preferences/blocks/general/animation.vue new file mode 100644 index 0000000..b27d074 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/general/animation.vue @@ -0,0 +1,51 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/general/general.vue b/packages/effects/layouts/src/widgets/preferences/blocks/general/general.vue new file mode 100644 index 0000000..630882f --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/general/general.vue @@ -0,0 +1,31 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/index.ts b/packages/effects/layouts/src/widgets/preferences/blocks/index.ts new file mode 100644 index 0000000..59595dc --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/index.ts @@ -0,0 +1,19 @@ +export { default as Block } from './block.vue'; +export { default as Animation } from './general/animation.vue'; +export { default as General } from './general/general.vue'; +export { default as Breadcrumb } from './layout/breadcrumb.vue'; +export { default as Content } from './layout/content.vue'; +export { default as Copyright } from './layout/copyright.vue'; +export { default as Footer } from './layout/footer.vue'; +export { default as Header } from './layout/header.vue'; +export { default as Layout } from './layout/layout.vue'; +export { default as Navigation } from './layout/navigation.vue'; +export { default as Sidebar } from './layout/sidebar.vue'; +export { default as Tabbar } from './layout/tabbar.vue'; +export { default as Widget } from './layout/widget.vue'; +export { default as GlobalShortcutKeys } from './shortcut-keys/global.vue'; +export { default as SwitchItem } from './switch-item.vue'; +export { default as BuiltinTheme } from './theme/builtin.vue'; +export { default as ColorMode } from './theme/color-mode.vue'; +export { default as Radius } from './theme/radius.vue'; +export { default as Theme } from './theme/theme.vue'; diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/input-item.vue b/packages/effects/layouts/src/widgets/preferences/blocks/input-item.vue new file mode 100644 index 0000000..0de02dc --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/input-item.vue @@ -0,0 +1,50 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/layout/breadcrumb.vue b/packages/effects/layouts/src/widgets/preferences/blocks/layout/breadcrumb.vue new file mode 100644 index 0000000..51f1a40 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/layout/breadcrumb.vue @@ -0,0 +1,56 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/layout/content.vue b/packages/effects/layouts/src/widgets/preferences/blocks/layout/content.vue new file mode 100644 index 0000000..d45417c --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/layout/content.vue @@ -0,0 +1,52 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/layout/copyright.vue b/packages/effects/layouts/src/widgets/preferences/blocks/layout/copyright.vue new file mode 100644 index 0000000..9389029 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/layout/copyright.vue @@ -0,0 +1,44 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/layout/footer.vue b/packages/effects/layouts/src/widgets/preferences/blocks/layout/footer.vue new file mode 100644 index 0000000..8a77920 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/layout/footer.vue @@ -0,0 +1,17 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/layout/header.vue b/packages/effects/layouts/src/widgets/preferences/blocks/layout/header.vue new file mode 100644 index 0000000..ee04ee2 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/layout/header.vue @@ -0,0 +1,74 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/layout/layout.vue b/packages/effects/layouts/src/widgets/preferences/blocks/layout/layout.vue new file mode 100644 index 0000000..3d1ced3 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/layout/layout.vue @@ -0,0 +1,109 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/layout/navigation.vue b/packages/effects/layouts/src/widgets/preferences/blocks/layout/navigation.vue new file mode 100644 index 0000000..23acb1f --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/layout/navigation.vue @@ -0,0 +1,45 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/layout/sidebar.vue b/packages/effects/layouts/src/widgets/preferences/blocks/layout/sidebar.vue new file mode 100644 index 0000000..49122ea --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/layout/sidebar.vue @@ -0,0 +1,65 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/layout/tabbar.vue b/packages/effects/layouts/src/widgets/preferences/blocks/layout/tabbar.vue new file mode 100644 index 0000000..ea533da --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/layout/tabbar.vue @@ -0,0 +1,94 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/layout/widget.vue b/packages/effects/layouts/src/widgets/preferences/blocks/layout/widget.vue new file mode 100644 index 0000000..82addb3 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/layout/widget.vue @@ -0,0 +1,71 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/number-field-item.vue b/packages/effects/layouts/src/widgets/preferences/blocks/number-field-item.vue new file mode 100644 index 0000000..3878f81 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/number-field-item.vue @@ -0,0 +1,74 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/select-item.vue b/packages/effects/layouts/src/widgets/preferences/blocks/select-item.vue new file mode 100644 index 0000000..f8e3384 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/select-item.vue @@ -0,0 +1,66 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/shortcut-keys/global.vue b/packages/effects/layouts/src/widgets/preferences/blocks/shortcut-keys/global.vue new file mode 100644 index 0000000..f71a1f6 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/shortcut-keys/global.vue @@ -0,0 +1,50 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/switch-item.vue b/packages/effects/layouts/src/widgets/preferences/blocks/switch-item.vue new file mode 100644 index 0000000..5758dc3 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/switch-item.vue @@ -0,0 +1,53 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/theme/builtin.vue b/packages/effects/layouts/src/widgets/preferences/blocks/theme/builtin.vue new file mode 100644 index 0000000..8a41763 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/theme/builtin.vue @@ -0,0 +1,150 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/theme/color-mode.vue b/packages/effects/layouts/src/widgets/preferences/blocks/theme/color-mode.vue new file mode 100644 index 0000000..9a41d4e --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/theme/color-mode.vue @@ -0,0 +1,26 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/theme/radius.vue b/packages/effects/layouts/src/widgets/preferences/blocks/theme/radius.vue new file mode 100644 index 0000000..4201ed6 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/theme/radius.vue @@ -0,0 +1,38 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/theme/theme.vue b/packages/effects/layouts/src/widgets/preferences/blocks/theme/theme.vue new file mode 100644 index 0000000..3e6c5fa --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/theme/theme.vue @@ -0,0 +1,82 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/toggle-item.vue b/packages/effects/layouts/src/widgets/preferences/blocks/toggle-item.vue new file mode 100644 index 0000000..df2c39f --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/toggle-item.vue @@ -0,0 +1,46 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/icons/content-compact.vue b/packages/effects/layouts/src/widgets/preferences/icons/content-compact.vue new file mode 100644 index 0000000..a1bcefd --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/icons/content-compact.vue @@ -0,0 +1,119 @@ + diff --git a/packages/effects/layouts/src/widgets/preferences/icons/full-content.vue b/packages/effects/layouts/src/widgets/preferences/icons/full-content.vue new file mode 100644 index 0000000..5cbcaca --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/icons/full-content.vue @@ -0,0 +1,50 @@ + diff --git a/packages/effects/layouts/src/widgets/preferences/icons/header-mixed-nav.vue b/packages/effects/layouts/src/widgets/preferences/icons/header-mixed-nav.vue new file mode 100644 index 0000000..d5c9367 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/icons/header-mixed-nav.vue @@ -0,0 +1,202 @@ + diff --git a/packages/effects/layouts/src/widgets/preferences/icons/header-nav.vue b/packages/effects/layouts/src/widgets/preferences/icons/header-nav.vue new file mode 100644 index 0000000..18158b3 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/icons/header-nav.vue @@ -0,0 +1,119 @@ + diff --git a/packages/effects/layouts/src/widgets/preferences/icons/header-sidebar-nav.vue b/packages/effects/layouts/src/widgets/preferences/icons/header-sidebar-nav.vue new file mode 100644 index 0000000..b44e249 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/icons/header-sidebar-nav.vue @@ -0,0 +1,177 @@ + diff --git a/packages/effects/layouts/src/widgets/preferences/icons/index.ts b/packages/effects/layouts/src/widgets/preferences/icons/index.ts new file mode 100644 index 0000000..7eec1b4 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/icons/index.ts @@ -0,0 +1,12 @@ +import HeaderNav from './header-nav.vue'; + +export { default as ContentCompact } from './content-compact.vue'; +export { default as FullContent } from './full-content.vue'; +export { default as HeaderMixedNav } from './header-mixed-nav.vue'; +export { default as HeaderSidebarNav } from './header-sidebar-nav.vue'; +export { default as MixedNav } from './mixed-nav.vue'; +export { default as SidebarMixedNav } from './sidebar-mixed-nav.vue'; +export { default as SidebarNav } from './sidebar-nav.vue'; + +const ContentWide = HeaderNav; +export { ContentWide, HeaderNav }; diff --git a/packages/effects/layouts/src/widgets/preferences/icons/mixed-nav.vue b/packages/effects/layouts/src/widgets/preferences/icons/mixed-nav.vue new file mode 100644 index 0000000..d194383 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/icons/mixed-nav.vue @@ -0,0 +1,161 @@ + diff --git a/packages/effects/layouts/src/widgets/preferences/icons/setting.vue b/packages/effects/layouts/src/widgets/preferences/icons/setting.vue new file mode 100644 index 0000000..d824e11 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/icons/setting.vue @@ -0,0 +1,12 @@ + diff --git a/packages/effects/layouts/src/widgets/preferences/icons/sidebar-mixed-nav.vue b/packages/effects/layouts/src/widgets/preferences/icons/sidebar-mixed-nav.vue new file mode 100644 index 0000000..8fc0ba4 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/icons/sidebar-mixed-nav.vue @@ -0,0 +1,173 @@ + diff --git a/packages/effects/layouts/src/widgets/preferences/icons/sidebar-nav.vue b/packages/effects/layouts/src/widgets/preferences/icons/sidebar-nav.vue new file mode 100644 index 0000000..83ff399 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/icons/sidebar-nav.vue @@ -0,0 +1,153 @@ + diff --git a/packages/effects/layouts/src/widgets/preferences/index.ts b/packages/effects/layouts/src/widgets/preferences/index.ts new file mode 100644 index 0000000..0a7d32c --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/index.ts @@ -0,0 +1,3 @@ +export { default as Preferences } from './preferences.vue'; +export { default as PreferencesButton } from './preferences-button.vue'; +export * from './use-open-preferences'; diff --git a/packages/effects/layouts/src/widgets/preferences/preferences-button.vue b/packages/effects/layouts/src/widgets/preferences/preferences-button.vue new file mode 100644 index 0000000..29e110d --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/preferences-button.vue @@ -0,0 +1,19 @@ + + diff --git a/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue b/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue new file mode 100644 index 0000000..88e8381 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue @@ -0,0 +1,446 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/preferences.vue b/packages/effects/layouts/src/widgets/preferences/preferences.vue new file mode 100644 index 0000000..bfab01d --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/preferences.vue @@ -0,0 +1,70 @@ + + diff --git a/packages/effects/layouts/src/widgets/preferences/use-open-preferences.ts b/packages/effects/layouts/src/widgets/preferences/use-open-preferences.ts new file mode 100644 index 0000000..eb9d847 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/use-open-preferences.ts @@ -0,0 +1,16 @@ +import { ref } from 'vue'; + +const openPreferences = ref(false); + +function useOpenPreferences() { + function handleOpenPreference() { + openPreferences.value = true; + } + + return { + handleOpenPreference, + openPreferences, + }; +} + +export { useOpenPreferences }; diff --git a/packages/effects/layouts/src/widgets/theme-toggle/index.ts b/packages/effects/layouts/src/widgets/theme-toggle/index.ts new file mode 100644 index 0000000..0673d21 --- /dev/null +++ b/packages/effects/layouts/src/widgets/theme-toggle/index.ts @@ -0,0 +1 @@ +export { default as ThemeToggle } from './theme-toggle.vue'; diff --git a/packages/effects/layouts/src/widgets/theme-toggle/theme-button.vue b/packages/effects/layouts/src/widgets/theme-toggle/theme-button.vue new file mode 100644 index 0000000..77f330c --- /dev/null +++ b/packages/effects/layouts/src/widgets/theme-toggle/theme-button.vue @@ -0,0 +1,185 @@ + + + + + diff --git a/packages/effects/layouts/src/widgets/theme-toggle/theme-toggle.vue b/packages/effects/layouts/src/widgets/theme-toggle/theme-toggle.vue new file mode 100644 index 0000000..4ddc6b8 --- /dev/null +++ b/packages/effects/layouts/src/widgets/theme-toggle/theme-toggle.vue @@ -0,0 +1,82 @@ + + diff --git a/packages/effects/layouts/src/widgets/user-dropdown/index.ts b/packages/effects/layouts/src/widgets/user-dropdown/index.ts new file mode 100644 index 0000000..86429e9 --- /dev/null +++ b/packages/effects/layouts/src/widgets/user-dropdown/index.ts @@ -0,0 +1 @@ +export { default as UserDropdown } from './user-dropdown.vue'; diff --git a/packages/effects/layouts/src/widgets/user-dropdown/user-dropdown.vue b/packages/effects/layouts/src/widgets/user-dropdown/user-dropdown.vue new file mode 100644 index 0000000..d57f0c5 --- /dev/null +++ b/packages/effects/layouts/src/widgets/user-dropdown/user-dropdown.vue @@ -0,0 +1,254 @@ + + + diff --git a/packages/effects/layouts/tsconfig.json b/packages/effects/layouts/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/packages/effects/layouts/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/effects/plugins/README.md b/packages/effects/plugins/README.md new file mode 100644 index 0000000..c394c9c --- /dev/null +++ b/packages/effects/plugins/README.md @@ -0,0 +1,28 @@ +# @vben/plugins + +该目录用于存放项目中集成的第三方库及其相关插件。每个插件都包含了可重用的逻辑、配置和组件,方便在项目中进行统一管理和调用。 + +## 注意 + +所有的第三方插件都必须以 `subpath` 形式引入,例: + +以 `echarts` 为例,引入方式如下: + +**packages.json** + +```json +"exports": { + "./echarts": { + "types": "./src/echarts/index.ts", + "default": "./src/echarts/index.ts" + } + } +``` + +**使用方式** + +```ts +import { useEcharts } from '@vben/plugins/echarts'; +``` + +这样做的好处是,应用可以自行选择是否使用插件,而不会因为插件的引入及副作用而导致打包体积增大,只引入需要的插件即可。 diff --git a/packages/effects/plugins/package.json b/packages/effects/plugins/package.json new file mode 100644 index 0000000..997e660 --- /dev/null +++ b/packages/effects/plugins/package.json @@ -0,0 +1,42 @@ +{ + "name": "@vben/plugins", + "version": "5.5.3", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/effects/plugins" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + "./echarts": { + "types": "./src/echarts/index.ts", + "default": "./src/echarts/index.ts" + }, + "./vxe-table": { + "types": "./src/vxe-table/index.ts", + "default": "./src/vxe-table/index.ts" + } + }, + "dependencies": { + "@vben-core/form-ui": "workspace:*", + "@vben-core/shadcn-ui": "workspace:*", + "@vben-core/shared": "workspace:*", + "@vben/hooks": "workspace:*", + "@vben/icons": "workspace:*", + "@vben/locales": "workspace:*", + "@vben/preferences": "workspace:*", + "@vben/types": "workspace:*", + "@vben/utils": "workspace:*", + "@vueuse/core": "catalog:", + "echarts": "catalog:", + "vue": "catalog:", + "vxe-pc-ui": "catalog:", + "vxe-table": "catalog:" + } +} diff --git a/packages/effects/plugins/src/echarts/echarts-ui.vue b/packages/effects/plugins/src/echarts/echarts-ui.vue new file mode 100644 index 0000000..70d1f20 --- /dev/null +++ b/packages/effects/plugins/src/echarts/echarts-ui.vue @@ -0,0 +1,15 @@ + + + diff --git a/packages/effects/plugins/src/echarts/echarts.ts b/packages/effects/plugins/src/echarts/echarts.ts new file mode 100644 index 0000000..a576b86 --- /dev/null +++ b/packages/effects/plugins/src/echarts/echarts.ts @@ -0,0 +1,70 @@ +import type { + // 系列类型的定义后缀都为 SeriesOption + BarSeriesOption, + LineSeriesOption, +} from 'echarts/charts'; +import type { + DatasetComponentOption, + GridComponentOption, + // 组件类型的定义后缀都为 ComponentOption + TitleComponentOption, + TooltipComponentOption, +} from 'echarts/components'; +import type { ComposeOption } from 'echarts/core'; + +import { + BarChart, + GaugeChart, + LineChart, + MapChart, + PieChart, + RadarChart, +} from 'echarts/charts'; +import { + // 数据集组件 + DatasetComponent, + GridComponent, + LegendComponent, + TitleComponent, + ToolboxComponent, + TooltipComponent, + // 内置数据转换器组件 (filter, sort) + TransformComponent, + VisualMapComponent, +} from 'echarts/components'; +import * as echarts from 'echarts/core'; +import { LabelLayout, UniversalTransition } from 'echarts/features'; +import { CanvasRenderer } from 'echarts/renderers'; + +// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型 +export type ECOption = ComposeOption< + | BarSeriesOption + | DatasetComponentOption + | GridComponentOption + | LineSeriesOption + | TitleComponentOption + | TooltipComponentOption +>; + +// 注册必须的组件 +echarts.use([ + TitleComponent, + PieChart, + RadarChart, + TooltipComponent, + GridComponent, + DatasetComponent, + TransformComponent, + BarChart, + LineChart, + LabelLayout, + UniversalTransition, + CanvasRenderer, + LegendComponent, + ToolboxComponent, + GaugeChart, + VisualMapComponent, + MapChart, +]); + +export default echarts; diff --git a/packages/effects/plugins/src/echarts/index.ts b/packages/effects/plugins/src/echarts/index.ts new file mode 100644 index 0000000..80f36a1 --- /dev/null +++ b/packages/effects/plugins/src/echarts/index.ts @@ -0,0 +1,3 @@ +export * from './echarts'; +export { default as EchartsUI } from './echarts-ui.vue'; +export * from './use-echarts'; diff --git a/packages/effects/plugins/src/echarts/use-echarts.ts b/packages/effects/plugins/src/echarts/use-echarts.ts new file mode 100644 index 0000000..d81bf8f --- /dev/null +++ b/packages/effects/plugins/src/echarts/use-echarts.ts @@ -0,0 +1,124 @@ +import type { EChartsOption } from 'echarts'; + +import type { Ref } from 'vue'; + +import type { Nullable } from '@vben/types'; + +import type EchartsUI from './echarts-ui.vue'; + +import { computed, nextTick, watch } from 'vue'; + +import { usePreferences } from '@vben/preferences'; + +import { + tryOnUnmounted, + useDebounceFn, + useResizeObserver, + useTimeoutFn, + useWindowSize, +} from '@vueuse/core'; + +import echarts from './echarts'; + +type EchartsUIType = typeof EchartsUI | undefined; + +type EchartsThemeType = 'dark' | 'light' | null; + +function useEcharts(chartRef: Ref) { + let chartInstance: echarts.ECharts | null = null; + let cacheOptions: EChartsOption = {}; + + const { isDark } = usePreferences(); + const { height, width } = useWindowSize(); + const resizeHandler: () => void = useDebounceFn(resize, 200); + + const getOptions = computed((): EChartsOption => { + if (!isDark.value) { + return {}; + } + + return { + backgroundColor: 'transparent', + }; + }); + + const initCharts = (t?: EchartsThemeType) => { + const el = chartRef?.value?.$el; + if (!el) { + return; + } + chartInstance = echarts.init(el, t || isDark.value ? 'dark' : null); + + return chartInstance; + }; + + const renderEcharts = ( + options: EChartsOption, + clear = true, + ): Promise> => { + cacheOptions = options; + const currentOptions = { + ...options, + ...getOptions.value, + }; + return new Promise((resolve) => { + if (chartRef.value?.offsetHeight === 0) { + useTimeoutFn(async () => { + resolve(await renderEcharts(currentOptions)); + }, 30); + return; + } + nextTick(() => { + useTimeoutFn(() => { + if (!chartInstance) { + const instance = initCharts(); + if (!instance) return; + } + clear && chartInstance?.clear(); + chartInstance?.setOption(currentOptions); + resolve(chartInstance); + }, 30); + }); + }); + }; + + function resize(withAnimation = true) { + chartInstance?.resize({ + animation: withAnimation + ? { + duration: 300, + easing: 'quadraticIn', + } + : undefined, + }); + } + + watch([width, height], () => { + resizeHandler?.(); + }); + + useResizeObserver(chartRef as never, resizeHandler); + + watch(isDark, () => { + if (chartInstance) { + chartInstance.dispose(); + initCharts(); + renderEcharts(cacheOptions); + resize(); + } + }); + + tryOnUnmounted(() => { + // 销毁实例,释放资源 + chartInstance?.dispose(); + }); + return { + renderEcharts, + resize, + getChartInstance: () => chartInstance, + }; +} + +export { useEcharts }; + +export type { EchartsUIType }; diff --git a/packages/effects/plugins/src/vxe-table/api.ts b/packages/effects/plugins/src/vxe-table/api.ts new file mode 100644 index 0000000..133dbb3 --- /dev/null +++ b/packages/effects/plugins/src/vxe-table/api.ts @@ -0,0 +1,126 @@ +import type { ExtendedFormApi } from '@vben-core/form-ui'; +import type { VxeGridInstance } from 'vxe-table'; + +import type { VxeGridProps } from './types'; + +import { Store } from '@vben-core/shared/store'; +import { + bindMethods, + isBoolean, + isFunction, + mergeWithArrayOverride, + StateHandler, +} from '@vben-core/shared/utils'; +import { toRaw } from 'vue'; + +function getDefaultState(): VxeGridProps { + return { + class: '', + gridClass: '', + gridOptions: {}, + gridEvents: {}, + formOptions: undefined, + showSearchForm: true, + }; +} + +export class VxeGridApi { + private isMounted = false; + + private stateHandler: StateHandler; + public formApi = {} as ExtendedFormApi; + + // private prevState: null | VxeGridProps = null; + public grid = {} as VxeGridInstance; + + public state: null | VxeGridProps = null; + + public store: Store; + + constructor(options: VxeGridProps = {}) { + const storeState = { ...options }; + + const defaultState = getDefaultState(); + this.store = new Store( + mergeWithArrayOverride(storeState, defaultState), + { + onUpdate: () => { + // this.prevState = this.state; + this.state = this.store.state; + }, + }, + ); + + this.state = this.store.state; + this.stateHandler = new StateHandler(); + bindMethods(this); + } + + mount(instance: null | VxeGridInstance, formApi: ExtendedFormApi) { + if (!this.isMounted && instance) { + this.grid = instance; + this.formApi = formApi; + this.stateHandler.setConditionTrue(); + this.isMounted = true; + } + } + + async query(params: Record = {}) { + try { + await this.grid.commitProxy('query', toRaw(params)); + } catch (error) { + console.error('Error occurred while querying:', error); + } + } + + async reload(params: Record = {}) { + try { + await this.grid.commitProxy('reload', toRaw(params)); + } catch (error) { + console.error('Error occurred while reloading:', error); + } + } + + setGridOptions(options: Partial) { + this.setState({ + gridOptions: options, + }); + } + + setLoading(isLoading: boolean) { + this.setState({ + gridOptions: { + loading: isLoading, + }, + }); + } + + setState( + stateOrFn: + | ((prev: VxeGridProps) => Partial) + | Partial, + ) { + if (isFunction(stateOrFn)) { + this.store.setState((prev) => { + return mergeWithArrayOverride(stateOrFn(prev), prev); + }); + } else { + this.store.setState((prev) => mergeWithArrayOverride(stateOrFn, prev)); + } + } + + toggleSearchForm(show?: boolean) { + this.setState({ + showSearchForm: isBoolean(show) ? show : !this.state?.showSearchForm, + }); + // nextTick(() => { + // this.grid.recalculate(); + // }); + return this.state?.showSearchForm; + } + + unmount() { + this.isMounted = false; + this.stateHandler.reset(); + } +} diff --git a/packages/effects/plugins/src/vxe-table/extends.ts b/packages/effects/plugins/src/vxe-table/extends.ts new file mode 100644 index 0000000..d20ecfa --- /dev/null +++ b/packages/effects/plugins/src/vxe-table/extends.ts @@ -0,0 +1,80 @@ +import type { Recordable } from '@vben/types'; +import type { VxeGridProps, VxeUIExport } from 'vxe-table'; + +import type { VxeGridApi } from './api'; + +import { formatDate, formatDateTime, isFunction } from '@vben/utils'; + +export function extendProxyOptions( + api: VxeGridApi, + options: VxeGridProps, + getFormValues: () => Recordable, +) { + [ + 'query', + 'querySuccess', + 'queryError', + 'queryAll', + 'queryAllSuccess', + 'queryAllError', + ].forEach((key) => { + extendProxyOption(key, api, options, getFormValues); + }); +} + +function extendProxyOption( + key: string, + api: VxeGridApi, + options: VxeGridProps, + getFormValues: () => Recordable, +) { + const { proxyConfig } = options; + const configFn = (proxyConfig?.ajax as Recordable)?.[key]; + if (!isFunction(configFn)) { + return options; + } + + const wrapperFn = async ( + params: Recordable, + customValues: Recordable, + ...args: Recordable[] + ) => { + const formValues = getFormValues(); + const data = await configFn( + params, + { + /** + * 开启toolbarConfig.refresh功能 + * 点击刷新按钮 这里的值为PointerEvent 会携带错误参数 + */ + ...(customValues instanceof PointerEvent ? {} : customValues), + ...formValues, + }, + ...args, + ); + return data; + }; + api.setState({ + gridOptions: { + proxyConfig: { + ajax: { + [key]: wrapperFn, + }, + }, + }, + }); +} + +export function extendsDefaultFormatter(vxeUI: VxeUIExport) { + vxeUI.formats.add('formatDate', { + tableCellFormatMethod({ cellValue }) { + return formatDate(cellValue); + }, + }); + + vxeUI.formats.add('formatDateTime', { + tableCellFormatMethod({ cellValue }) { + return formatDateTime(cellValue); + }, + }); +} diff --git a/packages/effects/plugins/src/vxe-table/index.ts b/packages/effects/plugins/src/vxe-table/index.ts new file mode 100644 index 0000000..650760d --- /dev/null +++ b/packages/effects/plugins/src/vxe-table/index.ts @@ -0,0 +1,10 @@ +export { setupVbenVxeTable } from './init'; +export type { VxeTableGridOptions } from './types'; +export * from './use-vxe-grid'; +export { default as VbenVxeGrid } from './use-vxe-grid.vue'; +export type { + VxeGridDefines, + VxeGridListeners, + VxeGridProps, + VxeGridPropTypes, +} from 'vxe-table'; diff --git a/packages/effects/plugins/src/vxe-table/init.ts b/packages/effects/plugins/src/vxe-table/init.ts new file mode 100644 index 0000000..11488f4 --- /dev/null +++ b/packages/effects/plugins/src/vxe-table/init.ts @@ -0,0 +1,128 @@ +import type { SetupVxeTable } from './types'; + +import { usePreferences } from '@vben/preferences'; +import { useVbenForm } from '@vben-core/form-ui'; +import { defineComponent, watch } from 'vue'; +import { + VxeButton, + VxeCheckbox, + + // VxeFormGather, + // VxeForm, + // VxeFormItem, + VxeIcon, + VxeInput, + VxeLoading, + VxeModal, + VxeNumberInput, + VxePager, + // VxeList, + // VxeModal, + // VxeOptgroup, + // VxeOption, + // VxePulldown, + // VxeRadio, + // VxeRadioButton, + VxeRadioGroup, + VxeSelect, + VxeTooltip, + VxeUI, + VxeUpload, + // VxeSwitch, + // VxeTextarea, +} from 'vxe-pc-ui'; +import enUS from 'vxe-pc-ui/lib/language/en-US'; +// 导入默认的语言 +import zhCN from 'vxe-pc-ui/lib/language/zh-CN'; +import { + VxeColgroup, + VxeColumn, + VxeGrid, + VxeTable, + VxeToolbar, +} from 'vxe-table'; + +import { extendsDefaultFormatter } from './extends'; + +// 是否加载过 +let isInit = false; + +// eslint-disable-next-line import/no-mutable-exports +export let useTableForm: typeof useVbenForm; + +// 部分组件,如果没注册,vxe-table 会报错,这里实际没用组件,只是为了不报错,同时可以减少打包体积 +const createVirtualComponent = (name = '') => { + return defineComponent({ + name, + }); +}; + +export function initVxeTable() { + if (isInit) { + return; + } + + VxeUI.component(VxeTable); + VxeUI.component(VxeColumn); + VxeUI.component(VxeColgroup); + VxeUI.component(VxeGrid); + VxeUI.component(VxeToolbar); + + VxeUI.component(VxeButton); + // VxeUI.component(VxeButtonGroup); + VxeUI.component(VxeCheckbox); + // VxeUI.component(VxeCheckboxGroup); + VxeUI.component(createVirtualComponent('VxeForm')); + // VxeUI.component(VxeFormGather); + // VxeUI.component(VxeFormItem); + VxeUI.component(VxeIcon); + VxeUI.component(VxeInput); + // VxeUI.component(VxeList); + VxeUI.component(VxeLoading); + VxeUI.component(VxeModal); + VxeUI.component(VxeNumberInput); + // VxeUI.component(VxeOptgroup); + // VxeUI.component(VxeOption); + VxeUI.component(VxePager); + // VxeUI.component(VxePulldown); + // VxeUI.component(VxeRadio); + // VxeUI.component(VxeRadioButton); + VxeUI.component(VxeRadioGroup); + VxeUI.component(VxeSelect); + // VxeUI.component(VxeSwitch); + // VxeUI.component(VxeTextarea); + VxeUI.component(VxeTooltip); + VxeUI.component(VxeUpload); + + isInit = true; +} + +export function setupVbenVxeTable(setupOptions: SetupVxeTable) { + const { configVxeTable, useVbenForm } = setupOptions; + + initVxeTable(); + useTableForm = useVbenForm; + + const preference = usePreferences(); + + const localMap = { + 'zh-CN': zhCN, + 'en-US': enUS, + }; + + watch( + [() => preference.theme.value, () => preference.locale.value], + ([theme, locale]) => { + VxeUI.setTheme(theme === 'dark' ? 'dark' : 'light'); + VxeUI.setI18n(locale, localMap[locale]); + VxeUI.setLanguage(locale); + }, + { + immediate: true, + }, + ); + + extendsDefaultFormatter(VxeUI); + + configVxeTable(VxeUI); +} diff --git a/packages/effects/plugins/src/vxe-table/style.css b/packages/effects/plugins/src/vxe-table/style.css new file mode 100644 index 0000000..31e5766 --- /dev/null +++ b/packages/effects/plugins/src/vxe-table/style.css @@ -0,0 +1,112 @@ +:root .vxe-grid { + --vxe-ui-font-color: hsl(var(--foreground)); + --vxe-ui-font-primary-color: hsl(var(--primary)); + + /* --vxe-ui-font-lighten-color: #babdc0; + --vxe-ui-font-darken-color: #86898e; */ + --vxe-ui-font-disabled-color: hsl(var(--foreground) / 50%); + + /* base */ + --vxe-ui-base-popup-border-color: hsl(var(--border)); + --vxe-ui-input-disabled-color: hsl(var(--border) / 60%); + + /* --vxe-ui-base-popup-box-shadow: 0px 12px 30px 8px rgb(0 0 0 / 50%); */ + + /* layout */ + --vxe-ui-layout-background-color: hsl(var(--background)); + --vxe-ui-table-resizable-line-color: hsl(var(--heavy)); + + /* --vxe-ui-table-fixed-left-scrolling-box-shadow: 8px 0px 10px -5px hsl(var(--accent)); + --vxe-ui-table-fixed-right-scrolling-box-shadow: -8px 0px 10px -5px hsl(var(--accent)); */ + + /* input */ + --vxe-ui-input-border-color: hsl(var(--border)); + + /* --vxe-ui-input-placeholder-color: #8d9095; */ + + /* --vxe-ui-input-disabled-background-color: #262727; */ + + /* loading */ + --vxe-ui-loading-background-color: hsl(var(--overlay-content)); + + /* table */ + --vxe-ui-table-header-background-color: hsl(0deg 0% 98%); + --vxe-ui-table-border-color: hsl(var(--border)); + --vxe-ui-table-row-hover-background-color: hsl(var(--accent-hover)); + --vxe-ui-table-row-striped-background-color: hsl(var(--accent) / 60%); + --vxe-ui-table-row-hover-striped-background-color: hsl(var(--accent)); + --vxe-ui-table-row-radio-checked-background-color: hsl(var(--accent)); + --vxe-ui-table-row-hover-radio-checked-background-color: hsl( + var(--accent-hover) + ); + --vxe-ui-table-row-checkbox-checked-background-color: hsl(var(--accent)); + --vxe-ui-table-row-hover-checkbox-checked-background-color: hsl( + var(--accent-hover) + ); + --vxe-ui-table-row-current-background-color: hsl(var(--accent)); + --vxe-ui-table-row-hover-current-background-color: hsl(var(--accent-hover)); + + /* --vxe-ui-table-fixed-scrolling-box-shadow-color: rgb(0 0 0 / 80%); */ +} + +html[data-vxe-ui-theme='dark'] .vxe-grid { + --vxe-ui-table-header-background-color: hsl(var(--accent) / 50%); +} + +.vxe-pager { + .vxe-pager--prev-btn:not(.is--disabled):active, + .vxe-pager--next-btn:not(.is--disabled):active, + .vxe-pager--num-btn:not(.is--disabled):active, + .vxe-pager--jump-prev:not(.is--disabled):active, + .vxe-pager--jump-next:not(.is--disabled):active, + .vxe-pager--prev-btn:not(.is--disabled):focus, + .vxe-pager--next-btn:not(.is--disabled):focus, + .vxe-pager--num-btn:not(.is--disabled):focus, + .vxe-pager--jump-prev:not(.is--disabled):focus, + .vxe-pager--jump-next:not(.is--disabled):focus { + color: hsl(var(--accent-foreground)); + background-color: hsl(var(--accent)); + border: 1px solid hsl(var(--border)); + box-shadow: 0 0 0 1px hsl(var(--border)); + } + + .vxe-pager--wrapper { + display: flex; + align-items: center; + } + + .vxe-pager--sizes { + margin-right: auto; + } +} + +.vxe-pager--wrapper { + @apply justify-center md:justify-end; +} + +.vxe-tools--operate { + margin-right: 0.25rem; + margin-left: 0.75rem; +} + +.vxe-table-custom--checkbox-option:hover { + background: none !important; +} + +.vxe-toolbar { + padding: 0; +} + +/** 关闭搜索表单 */ +.vxe-grid:not(.vxe-grid--form-wrapper form) .vxe-toolbar { + padding: 0.6em 0 !important; +} + +/** 开启搜索表单 */ +.vxe-grid:has(.vxe-grid--form-wrapper form) .vxe-toolbar { + padding: 0 0 0.6em !important; +} + +.vxe-tools--operate:not(:has(button)) { + margin-left: 0; +} diff --git a/packages/effects/plugins/src/vxe-table/types.ts b/packages/effects/plugins/src/vxe-table/types.ts new file mode 100644 index 0000000..cac73ed --- /dev/null +++ b/packages/effects/plugins/src/vxe-table/types.ts @@ -0,0 +1,75 @@ +import type { ClassType, DeepPartial } from '@vben/types'; +import type { VbenFormProps } from '@vben-core/form-ui'; +import type { Ref } from 'vue'; +import type { + VxeGridListeners, + VxeGridPropTypes, + VxeGridProps as VxeTableGridProps, + VxeUIExport, +} from 'vxe-table'; + +import type { VxeGridApi } from './api'; + +import { useVbenForm } from '@vben-core/form-ui'; + +export interface VxePaginationInfo { + currentPage: number; + pageSize: number; + total: number; +} + +interface ToolbarConfigOptions extends VxeGridPropTypes.ToolbarConfig { + /** 是否显示切换搜索表单的按钮 */ + search?: boolean; +} + +export interface VxeTableGridOptions extends VxeTableGridProps { + /** 工具栏配置 */ + toolbarConfig?: ToolbarConfigOptions; +} + +export interface VxeGridProps { + /** + * 标题 + */ + tableTitle?: string; + /** + * 标题帮助 + */ + tableTitleHelp?: string; + /** + * 组件class + */ + class?: ClassType; + /** + * vxe-grid class + */ + gridClass?: ClassType; + /** + * vxe-grid 配置 + */ + gridOptions?: DeepPartial; + /** + * vxe-grid 事件 + */ + gridEvents?: Partial; + /** + * 表单配置 + */ + formOptions?: VbenFormProps; + /** + * 显示搜索表单 + */ + showSearchForm?: boolean; +} + +export type ExtendedVxeGridApi = { + useStore: >( + selector?: (state: NoInfer) => T, + ) => Readonly>; +} & VxeGridApi; + +export interface SetupVxeTable { + configVxeTable: (ui: VxeUIExport) => void; + useVbenForm: typeof useVbenForm; +} diff --git a/packages/effects/plugins/src/vxe-table/use-vxe-grid.ts b/packages/effects/plugins/src/vxe-table/use-vxe-grid.ts new file mode 100644 index 0000000..a309f5a --- /dev/null +++ b/packages/effects/plugins/src/vxe-table/use-vxe-grid.ts @@ -0,0 +1,45 @@ +import type { ExtendedVxeGridApi, VxeGridProps } from './types'; + +import { defineComponent, h, onBeforeUnmount } from 'vue'; + +import { useStore } from '@vben-core/shared/store'; + +import { VxeGridApi } from './api'; +import VxeGrid from './use-vxe-grid.vue'; + +export function useVbenVxeGrid(options: VxeGridProps) { + // const IS_REACTIVE = isReactive(options); + const api = new VxeGridApi(options); + const extendedApi: ExtendedVxeGridApi = api as ExtendedVxeGridApi; + extendedApi.useStore = (selector) => { + return useStore(api.store, selector); + }; + + const Grid = defineComponent( + (props: VxeGridProps, { attrs, slots }) => { + onBeforeUnmount(() => { + api.unmount(); + }); + api.setState({ ...props, ...attrs }); + return () => h(VxeGrid, { ...props, ...attrs, api: extendedApi }, slots); + }, + { + inheritAttrs: false, + name: 'VbenVxeGrid', + }, + ); + // Add reactivity support + // if (IS_REACTIVE) { + // watch( + // () => options, + // () => { + // api.setState(options); + // }, + // { immediate: true }, + // ); + // } + + return [Grid, extendedApi] as const; +} + +export type UseVbenVxeGrid = typeof useVbenVxeGrid; diff --git a/packages/effects/plugins/src/vxe-table/use-vxe-grid.vue b/packages/effects/plugins/src/vxe-table/use-vxe-grid.vue new file mode 100644 index 0000000..b96b31d --- /dev/null +++ b/packages/effects/plugins/src/vxe-table/use-vxe-grid.vue @@ -0,0 +1,406 @@ + + + diff --git a/packages/effects/plugins/tsconfig.json b/packages/effects/plugins/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/packages/effects/plugins/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/effects/request/package.json b/packages/effects/request/package.json new file mode 100644 index 0000000..eb8fb7c --- /dev/null +++ b/packages/effects/request/package.json @@ -0,0 +1,32 @@ +{ + "name": "@vben/request", + "version": "5.5.3", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/effects/request" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben/locales": "workspace:*", + "@vben/utils": "workspace:*", + "axios": "catalog:", + "qs": "^6.13.1" + }, + "devDependencies": { + "@types/qs": "^6.9.17", + "axios-mock-adapter": "catalog:" + } +} diff --git a/packages/effects/request/src/index.ts b/packages/effects/request/src/index.ts new file mode 100644 index 0000000..e1805bf --- /dev/null +++ b/packages/effects/request/src/index.ts @@ -0,0 +1,3 @@ +export * from './request-client'; +export * from 'axios'; +export { stringify } from 'qs'; diff --git a/packages/effects/request/src/request-client/index.ts b/packages/effects/request/src/request-client/index.ts new file mode 100644 index 0000000..a44cd15 --- /dev/null +++ b/packages/effects/request/src/request-client/index.ts @@ -0,0 +1,3 @@ +export * from './preset-interceptors'; +export * from './request-client'; +export type * from './types'; diff --git a/packages/effects/request/src/request-client/modules/downloader.test.ts b/packages/effects/request/src/request-client/modules/downloader.test.ts new file mode 100644 index 0000000..d44dcbb --- /dev/null +++ b/packages/effects/request/src/request-client/modules/downloader.test.ts @@ -0,0 +1,86 @@ +import type { AxiosRequestConfig } from 'axios'; + +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { FileDownloader } from './downloader'; + +describe('fileDownloader', () => { + let fileDownloader: FileDownloader; + const mockAxiosInstance = { + get: vi.fn(), + } as any; + + beforeEach(() => { + fileDownloader = new FileDownloader(mockAxiosInstance); + }); + + it('should create an instance of FileDownloader', () => { + expect(fileDownloader).toBeInstanceOf(FileDownloader); + }); + + it('should download a file and return a Blob', async () => { + const url = 'https://example.com/file'; + const mockBlob = new Blob(['file content'], { type: 'text/plain' }); + const mockResponse: Blob = mockBlob; + + mockAxiosInstance.get.mockResolvedValueOnce(mockResponse); + + const result = await fileDownloader.download(url); + + expect(result).toBeInstanceOf(Blob); + expect(result).toEqual(mockBlob); + expect(mockAxiosInstance.get).toHaveBeenCalledWith(url, { + responseType: 'blob', + responseReturn: 'body', + }); + }); + + it('should merge provided config with default config', async () => { + const url = 'https://example.com/file'; + const mockBlob = new Blob(['file content'], { type: 'text/plain' }); + const mockResponse: Blob = mockBlob; + + mockAxiosInstance.get.mockResolvedValueOnce(mockResponse); + + const customConfig: AxiosRequestConfig = { + headers: { 'Custom-Header': 'value' }, + }; + + const result = await fileDownloader.download(url, customConfig); + expect(result).toBeInstanceOf(Blob); + expect(result).toEqual(mockBlob); + expect(mockAxiosInstance.get).toHaveBeenCalledWith(url, { + ...customConfig, + responseType: 'blob', + responseReturn: 'body', + }); + }); + + it('should handle errors gracefully', async () => { + const url = 'https://example.com/file'; + mockAxiosInstance.get.mockRejectedValueOnce(new Error('Network Error')); + await expect(fileDownloader.download(url)).rejects.toThrow('Network Error'); + }); + + it('should handle empty URL gracefully', async () => { + const url = ''; + mockAxiosInstance.get.mockRejectedValueOnce( + new Error('Request failed with status code 404'), + ); + + await expect(fileDownloader.download(url)).rejects.toThrow( + 'Request failed with status code 404', + ); + }); + + it('should handle null URL gracefully', async () => { + const url = null as unknown as string; + mockAxiosInstance.get.mockRejectedValueOnce( + new Error('Request failed with status code 404'), + ); + + await expect(fileDownloader.download(url)).rejects.toThrow( + 'Request failed with status code 404', + ); + }); +}); diff --git a/packages/effects/request/src/request-client/modules/downloader.ts b/packages/effects/request/src/request-client/modules/downloader.ts new file mode 100644 index 0000000..77e72c6 --- /dev/null +++ b/packages/effects/request/src/request-client/modules/downloader.ts @@ -0,0 +1,41 @@ +import type { RequestClient } from '../request-client'; +import type { RequestClientConfig } from '../types'; + +type DownloadRequestConfig = { + /** + * 定义期望获得的数据类型。 + * raw: 原始的AxiosResponse,包括headers、status等。 + * body: 只返回响应数据的BODY部分(Blob) + */ + responseReturn?: 'body' | 'raw'; +} & Omit; + +class FileDownloader { + private client: RequestClient; + + constructor(client: RequestClient) { + this.client = client; + } + /** + * 下载文件 + * @param url 文件的完整链接 + * @param config 配置信息,可选。 + * @returns 如果config.responseReturn为'body',则返回Blob(默认),否则返回RequestResponse + */ + public async download( + url: string, + config?: DownloadRequestConfig, + ): Promise { + const finalConfig: DownloadRequestConfig = { + responseReturn: 'body', + ...config, + responseType: 'blob', + }; + + const response = await this.client.get(url, finalConfig); + + return response; + } +} + +export { FileDownloader }; diff --git a/packages/effects/request/src/request-client/modules/interceptor.ts b/packages/effects/request/src/request-client/modules/interceptor.ts new file mode 100644 index 0000000..f6d2ad8 --- /dev/null +++ b/packages/effects/request/src/request-client/modules/interceptor.ts @@ -0,0 +1,40 @@ +import type { AxiosInstance, AxiosResponse } from 'axios'; + +import type { + RequestInterceptorConfig, + ResponseInterceptorConfig, +} from '../types'; + +const defaultRequestInterceptorConfig: RequestInterceptorConfig = { + fulfilled: (response) => response, + rejected: (error) => Promise.reject(error), +}; + +const defaultResponseInterceptorConfig: ResponseInterceptorConfig = { + fulfilled: (response: AxiosResponse) => response, + rejected: (error) => Promise.reject(error), +}; + +class InterceptorManager { + private axiosInstance: AxiosInstance; + + constructor(instance: AxiosInstance) { + this.axiosInstance = instance; + } + + addRequestInterceptor({ + fulfilled, + rejected, + }: RequestInterceptorConfig = defaultRequestInterceptorConfig) { + this.axiosInstance.interceptors.request.use(fulfilled, rejected); + } + + addResponseInterceptor({ + fulfilled, + rejected, + }: ResponseInterceptorConfig = defaultResponseInterceptorConfig) { + this.axiosInstance.interceptors.response.use(fulfilled, rejected); + } +} + +export { InterceptorManager }; diff --git a/packages/effects/request/src/request-client/modules/uploader.test.ts b/packages/effects/request/src/request-client/modules/uploader.test.ts new file mode 100644 index 0000000..8306656 --- /dev/null +++ b/packages/effects/request/src/request-client/modules/uploader.test.ts @@ -0,0 +1,118 @@ +import type { AxiosRequestConfig, AxiosResponse } from 'axios'; + +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { FileUploader } from './uploader'; + +describe('fileUploader', () => { + let fileUploader: FileUploader; + // Mock the AxiosInstance + const mockAxiosInstance = { + post: vi.fn(), + } as any; + + beforeEach(() => { + fileUploader = new FileUploader(mockAxiosInstance); + }); + + it('should create an instance of FileUploader', () => { + expect(fileUploader).toBeInstanceOf(FileUploader); + }); + + it('should upload a file and return the response', async () => { + const url = 'https://example.com/upload'; + const file = new File(['file content'], 'test.txt', { type: 'text/plain' }); + const mockResponse: AxiosResponse = { + config: {} as any, + data: { success: true }, + headers: {}, + status: 200, + statusText: 'OK', + }; + + ( + mockAxiosInstance.post as unknown as ReturnType + ).mockResolvedValueOnce(mockResponse); + + const result = await fileUploader.upload(url, { file }); + expect(result).toEqual(mockResponse); + expect(mockAxiosInstance.post).toHaveBeenCalledWith( + url, + expect.any(FormData), + { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }, + ); + }); + + it('should merge provided config with default config', async () => { + const url = 'https://example.com/upload'; + const file = new File(['file content'], 'test.txt', { type: 'text/plain' }); + const mockResponse: AxiosResponse = { + config: {} as any, + data: { success: true }, + headers: {}, + status: 200, + statusText: 'OK', + }; + + ( + mockAxiosInstance.post as unknown as ReturnType + ).mockResolvedValueOnce(mockResponse); + + const customConfig: AxiosRequestConfig = { + headers: { 'Custom-Header': 'value' }, + }; + + const result = await fileUploader.upload(url, { file }, customConfig); + expect(result).toEqual(mockResponse); + expect(mockAxiosInstance.post).toHaveBeenCalledWith( + url, + expect.any(FormData), + { + headers: { + 'Content-Type': 'multipart/form-data', + 'Custom-Header': 'value', + }, + }, + ); + }); + + it('should handle errors gracefully', async () => { + const url = 'https://example.com/upload'; + const file = new File(['file content'], 'test.txt', { type: 'text/plain' }); + ( + mockAxiosInstance.post as unknown as ReturnType + ).mockRejectedValueOnce(new Error('Network Error')); + + await expect(fileUploader.upload(url, { file })).rejects.toThrow( + 'Network Error', + ); + }); + + it('should handle empty URL gracefully', async () => { + const url = ''; + const file = new File(['file content'], 'test.txt', { type: 'text/plain' }); + ( + mockAxiosInstance.post as unknown as ReturnType + ).mockRejectedValueOnce(new Error('Request failed with status code 404')); + + await expect(fileUploader.upload(url, { file })).rejects.toThrow( + 'Request failed with status code 404', + ); + }); + + it('should handle null URL gracefully', async () => { + const url = null as unknown as string; + const file = new File(['file content'], 'test.txt', { type: 'text/plain' }); + ( + mockAxiosInstance.post as unknown as ReturnType + ).mockRejectedValueOnce(new Error('Request failed with status code 404')); + + await expect(fileUploader.upload(url, { file })).rejects.toThrow( + 'Request failed with status code 404', + ); + }); +}); diff --git a/packages/effects/request/src/request-client/modules/uploader.ts b/packages/effects/request/src/request-client/modules/uploader.ts new file mode 100644 index 0000000..ee5e7a2 --- /dev/null +++ b/packages/effects/request/src/request-client/modules/uploader.ts @@ -0,0 +1,34 @@ +import type { RequestClient } from '../request-client'; +import type { RequestClientConfig } from '../types'; + +class FileUploader { + private client: RequestClient; + + constructor(client: RequestClient) { + this.client = client; + } + + public async upload( + url: string, + data: Record & { file: Blob | File }, + config?: RequestClientConfig, + ): Promise { + const formData = new FormData(); + + Object.entries(data).forEach(([key, value]) => { + formData.append(key, value); + }); + + const finalConfig: RequestClientConfig = { + ...config, + headers: { + 'Content-Type': 'multipart/form-data', + ...config?.headers, + }, + }; + + return this.client.post(url, formData, finalConfig); + } +} + +export { FileUploader }; diff --git a/packages/effects/request/src/request-client/preset-interceptors.ts b/packages/effects/request/src/request-client/preset-interceptors.ts new file mode 100644 index 0000000..71ddc74 --- /dev/null +++ b/packages/effects/request/src/request-client/preset-interceptors.ts @@ -0,0 +1,165 @@ +import type { RequestClient } from './request-client'; +import type { MakeErrorMessageFn, ResponseInterceptorConfig } from './types'; + +import { $t } from '@vben/locales'; +import { isFunction } from '@vben/utils'; + +import axios from 'axios'; + +export const defaultResponseInterceptor = ({ + codeField = 'code', + dataField = 'data', + successCode = 0, +}: { + /** 响应数据中代表访问结果的字段名 */ + codeField: string; + /** 响应数据中装载实际数据的字段名,或者提供一个函数从响应数据中解析需要返回的数据 */ + dataField: ((response: any) => any) | string; + /** 当codeField所指定的字段值与successCode相同时,代表接口访问成功。如果提供一个函数,则返回true代表接口访问成功 */ + successCode: ((code: any) => boolean) | number | string; +}): ResponseInterceptorConfig => { + return { + fulfilled: (response) => { + const { config, data: responseData, status } = response; + + if (config.responseReturn === 'raw') { + return response; + } + + if (status >= 200 && status < 400) { + if (config.responseReturn === 'body') { + return responseData; + } else if ( + isFunction(successCode) + ? successCode(responseData[codeField]) + : responseData[codeField] === successCode + ) { + return isFunction(dataField) + ? dataField(responseData) + : responseData[dataField]; + } + } + throw Object.assign({}, response, { response }); + }, + }; +}; + +export const authenticateResponseInterceptor = ({ + client, + doReAuthenticate, + doRefreshToken, + enableRefreshToken, + formatToken, +}: { + client: RequestClient; + doReAuthenticate: () => Promise; + doRefreshToken: () => Promise; + enableRefreshToken: boolean; + formatToken: (token: string) => null | string; +}): ResponseInterceptorConfig => { + return { + rejected: async (error) => { + const { config, response } = error; + // 如果不是 401 错误,直接抛出异常 + if (response?.status !== 401) { + throw error; + } + // 判断是否启用了 refreshToken 功能 + // 如果没有启用或者已经是重试请求了,直接跳转到重新登录 + if (!enableRefreshToken || config.__isRetryRequest) { + await doReAuthenticate(); + throw error; + } + // 如果正在刷新 token,则将请求加入队列,等待刷新完成 + if (client.isRefreshing) { + return new Promise((resolve) => { + client.refreshTokenQueue.push((newToken: string) => { + config.headers.Authorization = formatToken(newToken); + resolve(client.request(config.url, { ...config })); + }); + }); + } + + // 标记开始刷新 token + client.isRefreshing = true; + // 标记当前请求为重试请求,避免无限循环 + config.__isRetryRequest = true; + + try { + const newToken = await doRefreshToken(); + + // 处理队列中的请求 + client.refreshTokenQueue.forEach((callback) => callback(newToken)); + // 清空队列 + client.refreshTokenQueue = []; + + return client.request(error.config.url, { ...error.config }); + } catch (refreshError) { + // 如果刷新 token 失败,处理错误(如强制登出或跳转登录页面) + client.refreshTokenQueue.forEach((callback) => callback('')); + client.refreshTokenQueue = []; + console.error('Refresh token failed, please login again.'); + await doReAuthenticate(); + + throw refreshError; + } finally { + client.isRefreshing = false; + } + }, + }; +}; + +export const errorMessageResponseInterceptor = ( + makeErrorMessage?: MakeErrorMessageFn, +): ResponseInterceptorConfig => { + return { + rejected: (error: any) => { + if (axios.isCancel(error)) { + return Promise.reject(error); + } + + const err: string = error?.toString?.() ?? ''; + let errMsg = ''; + if (err?.includes('Network Error')) { + errMsg = $t('ui.fallback.http.networkError'); + } else if (error?.message?.includes?.('timeout')) { + errMsg = $t('ui.fallback.http.requestTimeout'); + } + if (errMsg) { + makeErrorMessage?.(errMsg, error); + return Promise.reject(error); + } + + let errorMessage = ''; + const status = error?.response?.status; + + switch (status) { + case 400: { + errorMessage = $t('ui.fallback.http.badRequest'); + break; + } + case 401: { + errorMessage = $t('ui.fallback.http.unauthorized'); + break; + } + case 403: { + errorMessage = $t('ui.fallback.http.forbidden'); + break; + } + case 404: { + errorMessage = $t('ui.fallback.http.notFound'); + break; + } + case 408: { + errorMessage = $t('ui.fallback.http.requestTimeout'); + break; + } + default: { + errorMessage = $t('ui.fallback.http.internalServerError'); + } + } + makeErrorMessage?.(errorMessage, error); + return Promise.reject(error); + }, + }; +}; diff --git a/packages/effects/request/src/request-client/request-client.test.ts b/packages/effects/request/src/request-client/request-client.test.ts new file mode 100644 index 0000000..2d94525 --- /dev/null +++ b/packages/effects/request/src/request-client/request-client.test.ts @@ -0,0 +1,99 @@ +import axios from 'axios'; +import MockAdapter from 'axios-mock-adapter'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; + +import { RequestClient } from './request-client'; + +describe('requestClient', () => { + let mock: MockAdapter; + let requestClient: RequestClient; + + beforeEach(() => { + mock = new MockAdapter(axios); + requestClient = new RequestClient(); + }); + + afterEach(() => { + mock.reset(); + }); + + it('should successfully make a GET request', async () => { + mock.onGet('test/url').reply(200, { data: 'response' }); + + const response = await requestClient.get('test/url'); + + expect(response.data).toEqual({ data: 'response' }); + }); + + it('should successfully make a POST request', async () => { + const postData = { key: 'value' }; + const mockData = { data: 'response' }; + mock.onPost('/test/post', postData).reply(200, mockData); + const response = await requestClient.post('/test/post', postData); + expect(response.data).toEqual(mockData); + }); + + it('should successfully make a PUT request', async () => { + const putData = { key: 'updatedValue' }; + const mockData = { data: 'updated response' }; + mock.onPut('/test/put', putData).reply(200, mockData); + const response = await requestClient.put('/test/put', putData); + expect(response.data).toEqual(mockData); + }); + + it('should successfully make a DELETE request', async () => { + const mockData = { data: 'delete response' }; + mock.onDelete('/test/delete').reply(200, mockData); + const response = await requestClient.delete('/test/delete'); + expect(response.data).toEqual(mockData); + }); + + it('should handle network errors', async () => { + mock.onGet('/test/error').networkError(); + try { + await requestClient.get('/test/error'); + expect(true).toBe(false); + } catch (error: any) { + expect(error.isAxiosError).toBe(true); + expect(error.message).toBe('Network Error'); + } + }); + + it('should handle timeout', async () => { + mock.onGet('/test/timeout').timeout(); + try { + await requestClient.get('/test/timeout'); + expect(true).toBe(false); + } catch (error: any) { + expect(error.isAxiosError).toBe(true); + expect(error.code).toBe('ECONNABORTED'); + } + }); + + it('should successfully upload a file', async () => { + const fileData = new Blob(['file contents'], { type: 'text/plain' }); + + mock.onPost('/test/upload').reply((config) => { + return config.data instanceof FormData && config.data.has('file') + ? [200, { data: 'file uploaded' }] + : [400, { error: 'Bad Request' }]; + }); + + const response = await requestClient.upload('/test/upload', { + file: fileData, + }); + expect(response.data).toEqual({ data: 'file uploaded' }); + }); + + it('should successfully download a file as a blob', async () => { + const mockFileContent = new Blob(['mock file content'], { + type: 'text/plain', + }); + + mock.onGet('/test/download').reply(200, mockFileContent); + + const res = await requestClient.download('/test/download'); + + expect(res.data).toBeInstanceOf(Blob); + }); +}); diff --git a/packages/effects/request/src/request-client/request-client.ts b/packages/effects/request/src/request-client/request-client.ts new file mode 100644 index 0000000..9b0deaf --- /dev/null +++ b/packages/effects/request/src/request-client/request-client.ts @@ -0,0 +1,172 @@ +/* + * @Author: Mm + * @Date: 2025-05-06 14:08:27 + * @LastEditors: Mm + * @LastEditTime: 2025-07-30 09:16:50 + * 不写bug的程序员不是一个好程序员! + */ +import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'; + +import type { RequestClientConfig, RequestClientOptions } from './types'; + +import { bindMethods, merge } from '@vben/utils'; + +import axios from 'axios'; + +import { FileDownloader } from './modules/downloader'; +import { InterceptorManager } from './modules/interceptor'; +import { FileUploader } from './modules/uploader'; + +class RequestClient { + public addRequestInterceptor: InterceptorManager['addRequestInterceptor']; + + public addResponseInterceptor: InterceptorManager['addResponseInterceptor']; + public download: FileDownloader['download']; + + // 是否正在刷新token + public isRefreshing = false; + // 刷新token队列 + public refreshTokenQueue: ((token: string) => void)[] = []; + public upload: FileUploader['upload']; + private readonly instance: AxiosInstance; + + /** + * 构造函数,用于创建Axios实例 + * @param options - Axios请求配置,可选 + */ + constructor(options: RequestClientOptions = {}) { + // 合并默认配置和传入的配置 + const defaultConfig: RequestClientOptions = { + headers: { + 'Content-Type': 'application/json;charset=utf-8', + }, + responseReturn: 'raw', + // 默认超时时间 + timeout: 10_000*60*60, // 1小时 + }; + const { ...axiosConfig } = options; + const requestConfig = merge(axiosConfig, defaultConfig); + this.instance = axios.create(requestConfig); + + bindMethods(this); + + // 实例化拦截器管理器 + const interceptorManager = new InterceptorManager(this.instance); + this.addRequestInterceptor = + interceptorManager.addRequestInterceptor.bind(interceptorManager); + this.addResponseInterceptor = + interceptorManager.addResponseInterceptor.bind(interceptorManager); + + // 实例化文件上传器 + const fileUploader = new FileUploader(this); + this.upload = fileUploader.upload.bind(fileUploader); + // 实例化文件下载器 + const fileDownloader = new FileDownloader(this); + this.download = fileDownloader.download.bind(fileDownloader); + } + + /** + * DELETE请求方法 + */ + public delete( + url: string, + config?: RequestClientConfig, + ): Promise { + return this.request(url, { ...config, method: 'DELETE' }); + } + + /** + * DELETE请求方法 成功会弹出msg + */ + public deleteWithMsg( + url: string, + config?: AxiosRequestConfig, + ): Promise { + return this.request(url, { + ...config, + method: 'DELETE', + successMessageMode: 'message', + }); + } + + /** + * GET请求方法 + */ + public get(url: string, config?: RequestClientConfig): Promise { + return this.request(url, { ...config, method: 'GET' }); + } + + /** + * POST请求方法 + */ + public post( + url: string, + data?: any, + config?: RequestClientConfig, + ): Promise { + return this.request(url, { ...config, data, method: 'POST' }); + } + + /** + * POST请求方法 成功会弹出msg + */ + public postWithMsg( + url: string, + data?: any, + config?: AxiosRequestConfig, + ): Promise { + return this.request(url, { + ...config, + data, + method: 'POST', + successMessageMode: 'message', + }); + } + + /** + * PUT请求方法 + */ + public put( + url: string, + data?: any, + config?: RequestClientConfig, + ): Promise { + return this.request(url, { ...config, data, method: 'PUT' }); + } + + /** + * PUT请求方法 成功会弹出msg + */ + public putWithMsg( + url: string, + data?: any, + config?: AxiosRequestConfig, + ): Promise { + return this.request(url, { + ...config, + data, + method: 'PUT', + successMessageMode: 'message', + }); + } + + /** + * 通用的请求方法 + */ + public async request( + url: string, + config: RequestClientConfig, + ): Promise { + try { + const response: AxiosResponse = await this.instance({ + url, + ...config, + }); + return response as T; + } catch (error: any) { + throw error.response ? error.response.data : error; + } + } +} + +export { RequestClient }; diff --git a/packages/effects/request/src/request-client/types.ts b/packages/effects/request/src/request-client/types.ts new file mode 100644 index 0000000..5f5b49e --- /dev/null +++ b/packages/effects/request/src/request-client/types.ts @@ -0,0 +1,92 @@ +import type { + AxiosRequestConfig, + AxiosResponse, + CreateAxiosDefaults, + InternalAxiosRequestConfig, +} from 'axios'; + +type ExtendOptions = { + /** 响应数据的返回方式。 + * raw: 原始的AxiosResponse,包括headers、status等,不做是否成功请求的检查。 + * body: 返回响应数据的BODY部分(只会根据status检查请求是否成功,忽略对code的判断,这种情况下应由调用方检查请求是否成功)。 + * data: 解构响应的BODY数据,只返回其中的data节点数据(会检查status和code是否为成功状态)。 + */ + responseReturn?: 'body' | 'data' | 'raw'; +}; +type RequestClientConfig = AxiosRequestConfig & ExtendOptions; + +type RequestResponse = AxiosResponse & { + config: RequestClientConfig; +}; + +type RequestContentType = + | 'application/json;charset=utf-8' + | 'application/octet-stream;charset=utf-8' + | 'application/x-www-form-urlencoded;charset=utf-8' + | 'multipart/form-data;charset=utf-8'; + +type RequestClientOptions = CreateAxiosDefaults & ExtendOptions; + +interface RequestInterceptorConfig { + fulfilled?: ( + config: ExtendOptions & InternalAxiosRequestConfig, + ) => + | (ExtendOptions & InternalAxiosRequestConfig) + | Promise>; + rejected?: (error: any) => any; +} + +interface ResponseInterceptorConfig { + fulfilled?: ( + response: RequestResponse, + ) => Promise | RequestResponse; + rejected?: (error: any) => any; +} + +type MakeErrorMessageFn = (message: string, error: any) => void; + +interface HttpResponse { + code: number; + data: T; + msg: string; +} + +export type { + HttpResponse, + MakeErrorMessageFn, + RequestClientConfig, + RequestClientOptions, + RequestContentType, + RequestInterceptorConfig, + RequestResponse, + ResponseInterceptorConfig, +}; + +export type ErrorMessageMode = 'message' | 'modal' | 'none' | undefined; +export type SuccessMessageMode = ErrorMessageMode; + +/** + * 拓展axios的请求配置 + */ +declare module 'axios' { + interface AxiosRequestConfig { + /** 是否加密请求参数 */ + encrypt?: boolean; + /** + * 错误弹窗类型 + */ + errorMessageMode?: ErrorMessageMode; + /** + * 是否返回原生axios响应 + */ + isReturnNativeResponse?: boolean; + /** + * 是否需要转换响应 即只获取{code, msg, data}中的data + */ + isTransformResponse?: boolean; + /** + * 成功弹窗类型 + */ + successMessageMode?: SuccessMessageMode; + } +} diff --git a/packages/effects/request/tsconfig.json b/packages/effects/request/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/packages/effects/request/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/icons/README.md b/packages/icons/README.md new file mode 100644 index 0000000..bc50d25 --- /dev/null +++ b/packages/icons/README.md @@ -0,0 +1,19 @@ +# @vben/icons + +用于多个 `app` 公用的图标文件,继承了 `@vben-core/icons` 的所有能力。业务上有通用图标可以放在这里。 + +## 用法 + +### 添加依赖 + +```bash +# 进入目标应用目录,例如 apps/xxxx-app +# cd apps/xxxx-app +pnpm add @vben/icons +``` + +### 使用 + +```ts +import { X } from '@vben/icons'; +``` diff --git a/packages/icons/package.json b/packages/icons/package.json new file mode 100644 index 0000000..d40cfc7 --- /dev/null +++ b/packages/icons/package.json @@ -0,0 +1,60 @@ +{ + "name": "@vben/icons", + "version": "5.5.3", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/icons" + }, + "license": "MIT", + "type": "module", + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben-core/icons": "workspace:*", + "@vben-core/shadcn-ui": "workspace:^" + }, + "devDependencies": { + "@iconify/icons-akar-icons": "^1.2.19", + "@iconify/icons-ant-design": "^1.2.7", + "@iconify/icons-arcticons": "^1.2.77", + "@iconify/icons-bi": "^1.2.19", + "@iconify/icons-bx": "^1.2.6", + "@iconify/icons-carbon": "^1.2.20", + "@iconify/icons-devicon": "^1.2.17", + "@iconify/icons-emojione": "^1.2.6", + "@iconify/icons-eos-icons": "^1.2.6", + "@iconify/icons-fa-brands": "^1.2.4", + "@iconify/icons-fe": "^1.2.5", + "@iconify/icons-flat-color-icons": "^1.2.5", + "@iconify/icons-fluent": "^1.2.38", + "@iconify/icons-fluent-mdl2": "^1.2.1", + "@iconify/icons-ic": "^1.2.13", + "@iconify/icons-icon-park-outline": "^1.2.11", + "@iconify/icons-icon-park-twotone": "^1.2.8", + "@iconify/icons-la": "^1.2.3", + "@iconify/icons-logos": "^1.2.36", + "@iconify/icons-lucide": "^1.2.135", + "@iconify/icons-majesticons": "^1.2.6", + "@iconify/icons-material-symbols": "^1.2.58", + "@iconify/icons-mdi": "^1.2.48", + "@iconify/icons-mingcute": "^1.2.9", + "@iconify/icons-noto": "^1.2.10", + "@iconify/icons-ph": "^1.2.5", + "@iconify/icons-ri": "^1.2.10", + "@iconify/icons-simple-icons": "^1.2.74", + "@iconify/icons-skill-icons": "^1.2.1", + "@iconify/icons-solar": "^1.2.3", + "@iconify/icons-streamline": "^1.2.3", + "@iconify/icons-tabler": "^1.2.95", + "@iconify/icons-uiw": "^1.2.6", + "@iconify/icons-vscode-icons": "^1.2.29", + "@iconify/icons-wpf": "^1.2.3" + } +} diff --git a/packages/icons/src/iconify-offline/index.ts b/packages/icons/src/iconify-offline/index.ts new file mode 100644 index 0000000..6e2e19e --- /dev/null +++ b/packages/icons/src/iconify-offline/index.ts @@ -0,0 +1,186 @@ +import { createIconifyOfflineIcon } from '@vben-core/icons'; + +import githubOutlined from '@iconify/icons-ant-design/github-outlined'; +import inboxIcon from '@iconify/icons-ant-design/inbox-outlined'; +import userOutlined from '@iconify/icons-ant-design/user-outlined'; +import ucIcon from '@iconify/icons-arcticons/uc-browser'; +import defaultFileIcon from '@iconify/icons-bx/file'; +import sqlIcon from '@iconify/icons-carbon/sql'; +import linuxIcon from '@iconify/icons-devicon/linux'; +import windowsIcon from '@iconify/icons-devicon/windows8'; +import alipayIcon from '@iconify/icons-fa-brands/alipay'; +import androidIcon from '@iconify/icons-flat-color-icons/android-os'; +import comandLine from '@iconify/icons-flat-color-icons/command-line'; +import folderIcon from '@iconify/icons-flat-color-icons/folder'; +import defaultOsIcon from '@iconify/icons-ic/outline-computer'; +import memoryIcon from '@iconify/icons-la/memory'; +import chromeIcon from '@iconify/icons-logos/chrome'; +import firefoxIcon from '@iconify/icons-logos/firefox'; +import edgeIcon from '@iconify/icons-logos/microsoft-edge'; +import operaIcon from '@iconify/icons-logos/opera'; +import quarkIcon from '@iconify/icons-logos/quarkus-icon'; +import redisIcon from '@iconify/icons-logos/redis'; +import safariIcon from '@iconify/icons-logos/safari'; +import vueIcon from '@iconify/icons-logos/vue'; +import iphoneIcon from '@iconify/icons-majesticons/iphone-x-apps-line'; +import menuIcon from '@iconify/icons-material-symbols/menu'; +import okButtonIcon from '@iconify/icons-mdi/button-pointer'; +import micromessengerIcon from '@iconify/icons-mdi/wechat'; +import defaultBrowserIcon from '@iconify/icons-ph/browser-duotone'; +import baiduIcon from '@iconify/icons-ri/baidu-fill'; +import dingdingFill from '@iconify/icons-ri/dingding-fill'; +import dingtalkIcon from '@iconify/icons-ri/dingding-line'; +import taobaoIconFill from '@iconify/icons-ri/taobao-fill'; +import giteeIcon from '@iconify/icons-simple-icons/gitee'; +import qqIcon from '@iconify/icons-simple-icons/tencentqq'; +import javaIcon from '@iconify/icons-skill-icons/java-light'; +import tsIcon from '@iconify/icons-skill-icons/typescript'; +import xmlIcon from '@iconify/icons-tabler/file-type-xml'; +import githubOAuthIcon from '@iconify/icons-uiw/github'; +import excelIcon from '@iconify/icons-vscode-icons/file-type-excel'; +import osxIcon from '@iconify/icons-wpf/macos'; + +import './menu-icons'; + +// 用户 下拉菜单 +export const GitHubOutlined = createIconifyOfflineIcon( + 'ant-design:github-outlined', + githubOutlined, +); + +export const UserOutlined = createIconifyOfflineIcon( + 'ant-design:user-outlined', + userOutlined, +); + +// 缓存监控使用 +export const RedisIcon = createIconifyOfflineIcon('logos:redis', redisIcon); +export const CommandLineIcon = createIconifyOfflineIcon( + 'flat-color-icons:command-line', + comandLine, +); +export const MemoryIcon = createIconifyOfflineIcon('la:memory', memoryIcon); + +// 用户管理 导入 +// Excel图标 +export const ExcelIcon = createIconifyOfflineIcon( + 'vscode-icons:file-type-excel', + excelIcon, +); +// 拖拽上传图标 +export const InBoxIcon = createIconifyOfflineIcon( + 'ant-design:inbox-outlined', + inboxIcon, +); + +// 第三方登录相关图标 +export const TaobaoIcon = createIconifyOfflineIcon( + 'ri:taobao-fill', + taobaoIconFill, +); +export const AlipayIcon = createIconifyOfflineIcon( + 'fa-brands:alipay', + alipayIcon, +); +export const DingdingIcon = createIconifyOfflineIcon( + 'ri:dingding-fill', + dingdingFill, +); +export const GiteeIcon = createIconifyOfflineIcon( + 'simple-icons:gitee', + giteeIcon, +); +export const GithubOAuthIcon = createIconifyOfflineIcon( + 'uiw:github', + githubOAuthIcon, +); + +// 系统相关图标 +export const WindowsIcon = createIconifyOfflineIcon( + 'devicon:windows8', + windowsIcon, +); +export const LinuxIcon = createIconifyOfflineIcon('devicon:linux', linuxIcon); +export const OSXIcon = createIconifyOfflineIcon('wpf:macos', osxIcon); +export const AndroidIcon = createIconifyOfflineIcon( + 'flat-color-icons:android-os', + androidIcon, +); +export const IPhoneIcon = createIconifyOfflineIcon( + 'majesticons:iphone-x-apps-line', + iphoneIcon, +); +// 上面图标没找到 默认图标 +export const DefaultOsIcon = createIconifyOfflineIcon( + 'ic:outline-computer', + defaultOsIcon, +); + +// 浏览器相关图标 +export const ChromeIcon = createIconifyOfflineIcon('logos:chrome', chromeIcon); +export const EdgeIcon = createIconifyOfflineIcon( + 'logos:microsoft-edge', + edgeIcon, +); +export const FirefoxIcon = createIconifyOfflineIcon( + 'logos:firefox', + firefoxIcon, +); +export const OperaIcon = createIconifyOfflineIcon('logos:opera', operaIcon); +export const SafariIcon = createIconifyOfflineIcon('logos:safari', safariIcon); +export const MicromessengerIcon = createIconifyOfflineIcon( + 'mdi:wechat', + micromessengerIcon, +); +export const QuarkIcon = createIconifyOfflineIcon( + 'logos:quarkus-icon', + quarkIcon, +); +export const QQIcon = createIconifyOfflineIcon( + 'simple-icons:tencentqq', + qqIcon, +); +export const DingtalkIcon = createIconifyOfflineIcon( + 'ri:dingding-line', + dingtalkIcon, +); +export const UcIcon = createIconifyOfflineIcon('arcticons:uc-browser', ucIcon); +export const BaiduIcon = createIconifyOfflineIcon('ri:baidu-fill', baiduIcon); +// 未知浏览器图标 +export const DefaultBrowserIcon = createIconifyOfflineIcon( + 'ph:browser-duotone', + defaultBrowserIcon, +); + +// 菜单类型 目录/按钮/菜单 +export const FolderIcon = createIconifyOfflineIcon( + 'flat-color-icons:folder', + folderIcon, +); +export const OkButtonIcon = createIconifyOfflineIcon( + 'mdi:button-pointer', + okButtonIcon, +); +export const MenuIcon = createIconifyOfflineIcon( + 'material-symbols:menu', + menuIcon, +); + +export const JavaIcon = createIconifyOfflineIcon( + 'skill-icons:java-light', + javaIcon, +); +export const XmlIcon = createIconifyOfflineIcon( + 'tabler:file-type-xml', + xmlIcon, +); +export const SqlIcon = createIconifyOfflineIcon('carbon:sql', sqlIcon); +export const TsIcon = createIconifyOfflineIcon( + 'skill-icons:typescript', + tsIcon, +); +export const VueIcon = createIconifyOfflineIcon('logos:vue', vueIcon); +export const DefaultFileIcon = createIconifyOfflineIcon( + 'flat-color-icons:folder', + defaultFileIcon, +); diff --git a/packages/icons/src/iconify-offline/menu-icons.ts b/packages/icons/src/iconify-offline/menu-icons.ts new file mode 100644 index 0000000..4f935e0 --- /dev/null +++ b/packages/icons/src/iconify-offline/menu-icons.ts @@ -0,0 +1,108 @@ +import { addIcon } from '@vben-core/icons'; + +import schedule from '@iconify/icons-akar-icons/schedule'; +import settingOutline from '@iconify/icons-ant-design/setting-outlined'; +import Operation from '@iconify/icons-arcticons/one-hand-operation'; +import BaseLineHousesFill from '@iconify/icons-bi/houses-fill'; +import BxPackage from '@iconify/icons-bx/package'; +import modelAlt from '@iconify/icons-carbon/model-alt'; +import taskApproved from '@iconify/icons-carbon/task-approved'; +import redisWordmark from '@iconify/icons-devicon/redis-wordmark'; +import springWordmark from '@iconify/icons-devicon/spring-wordmark'; +import vscode from '@iconify/icons-devicon/vscode'; +import evergreenTree from '@iconify/icons-emojione/evergreen-tree'; +import RoleBindingOutlined from '@iconify/icons-eos-icons/role-binding-outlined'; +import SystemGroup from '@iconify/icons-eos-icons/system-group'; +import NoticePush from '@iconify/icons-fe/notice-push'; +import leave from '@iconify/icons-flat-color-icons/leave'; +import plus from '@iconify/icons-flat-color-icons/plus'; +import builDefinition from '@iconify/icons-fluent-mdl2/build-definition'; +import Dictionary from '@iconify/icons-fluent-mdl2/dictionary'; +import flow from '@iconify/icons-fluent-mdl2/flow'; +import leaveUser from '@iconify/icons-fluent-mdl2/leave-user'; +import from24 from '@iconify/icons-fluent/form-24-regular'; +import BaseLineHouse from '@iconify/icons-ic/baseline-house'; +import monitor from '@iconify/icons-ic/baseline-monitor'; +import roundLaunch from '@iconify/icons-ic/round-launch'; +import MenuSharp from '@iconify/icons-ic/sharp-menu'; +import Appointment from '@iconify/icons-icon-park-outline/appointment'; +import SettingTwo from '@iconify/icons-icon-park-twotone/setting-two'; +import boolOpenText from '@iconify/icons-lucide/book-open-text'; +import copyright from '@iconify/icons-lucide/copyright'; +import table from '@iconify/icons-lucide/table'; +import cloudDoneOutlineRounded from '@iconify/icons-material-symbols/cloud-done-outline-rounded'; +import generatingTokensOutline from '@iconify/icons-material-symbols/generating-tokens-outline'; +import LogoDevOutline from '@iconify/icons-material-symbols/logo-dev-outline'; +import ccOutline from '@iconify/icons-mdi/cc-outline'; +import tools from '@iconify/icons-mdi/tools'; +import workflowOutline from '@iconify/icons-mdi/workflow-outline'; +import DepartmentLine from '@iconify/icons-mingcute/department-line'; +import profileLine from '@iconify/icons-mingcute/profile-line'; +import UserDuotone from '@iconify/icons-ph/user-duotone'; +import insatnceLine from '@iconify/icons-ri/instance-line'; +import todoLine from '@iconify/icons-ri/todo-line'; +import Authy from '@iconify/icons-simple-icons/authy'; +import FolderWithFilesOutline from '@iconify/icons-solar/folder-with-files-outline'; +import monitorBoldDuotone from '@iconify/icons-solar/monitor-bold-duotone'; +import InterfaceLoginDialPadFingerPasswordDialPadDotFinger from '@iconify/icons-streamline/interface-login-dial-pad-finger-password-dial-pad-dot-finger'; +import categoryPlus from '@iconify/icons-tabler/category-plus'; +import code from '@iconify/icons-tabler/code'; + +/** + * 这里添加菜单图标 + */ +addIcon('eos-icons:system-group', SystemGroup); +addIcon('ph:user-duotone', UserDuotone); +addIcon('eos-icons:role-binding-outlined', RoleBindingOutlined); +addIcon('ic:sharp-menu', MenuSharp); +addIcon('mingcute:department-line', DepartmentLine); +addIcon('icon-park-outline:appointment', Appointment); +addIcon('fluent-mdl2:dictionary', Dictionary); +addIcon('icon-park-twotone:setting-two', SettingTwo); +addIcon('fe:notice-push', NoticePush); +addIcon('material-symbols:logo-dev-outline', LogoDevOutline); +addIcon('arcticons:one-hand-operation', Operation); +addIcon( + 'streamline:interface-login-dial-pad-finger-password-dial-pad-dot-finger', + InterfaceLoginDialPadFingerPasswordDialPadDotFinger, +); +addIcon('solar:folder-with-files-outline', FolderWithFilesOutline); +addIcon('simple-icons:authy', Authy); +addIcon('ic:baseline-house', BaseLineHouse); +addIcon('bi:houses-fill', BaseLineHousesFill); +addIcon('bx:package', BxPackage); +addIcon('solar:monitor-bold-duotone', monitorBoldDuotone); +addIcon('material-symbols:generating-tokens-outline', generatingTokensOutline); +addIcon('devicon:redis-wordmark', redisWordmark); +addIcon('devicon:spring-wordmark', springWordmark); +addIcon('akar-icons:schedule', schedule); +addIcon('mdi:tools', tools); +addIcon('tabler:code', code); +addIcon('flat-color-icons:plus', plus); +addIcon('devicon:vscode', vscode); +addIcon('lucide:table', table); +addIcon('emojione:evergreen-tree', evergreenTree); +addIcon('fluent-mdl2:leave-user', leaveUser); +addIcon('mdi:workflow-outline', workflowOutline); +addIcon('tabler:category-plus', categoryPlus); +addIcon('carbon:model-alt', modelAlt); +addIcon('fluent-mdl2:build-definition', builDefinition); +addIcon('fluent-mdl2:build-definition', builDefinition); +addIcon('icon-park-outline:monitor', monitor); +addIcon('ri:instance-line', insatnceLine); +addIcon('ri:todo-line', todoLine); +addIcon('fluent:form-24-regular', from24); +addIcon('carbon:task-approved', taskApproved); +addIcon('ic:round-launch', roundLaunch); +addIcon('material-symbols:cloud-done-outline-rounded', cloudDoneOutlineRounded); +addIcon('mdi:cc-outline', ccOutline); +addIcon('lucide:book-open-text', boolOpenText); +addIcon('lucide:copyright', copyright); +// 个人中心 +addIcon('mingcute:profile-line', profileLine); +// oss配置 +addIcon('ant-design:setting-outlined', settingOutline); +// 请假 +addIcon('flat-color-icons:leave', leave); +// flow +addIcon('fluent-mdl2:flow', flow); diff --git a/packages/icons/src/iconify/index.ts b/packages/icons/src/iconify/index.ts new file mode 100644 index 0000000..1e59ef0 --- /dev/null +++ b/packages/icons/src/iconify/index.ts @@ -0,0 +1,18 @@ +import { createIconifyIcon } from '@vben-core/icons'; + +export * from '@vben-core/icons'; + +export const MdiKeyboardEsc = createIconifyIcon('mdi:keyboard-esc'); + +export const MdiWechat = createIconifyIcon('mdi:wechat'); + +export const MdiGithub = createIconifyIcon('mdi:github'); + +export const MdiGoogle = createIconifyIcon('mdi:google'); + +export const MdiQqchat = createIconifyIcon('mdi:qqchat'); + +export const EosSystem = createIconifyIcon('eos-icons:system-group'); + +// 个人中心 +export const ProfileIcon = createIconifyIcon('mingcute:profile-line'); diff --git a/packages/icons/src/icons/empty-icon.vue b/packages/icons/src/icons/empty-icon.vue new file mode 100644 index 0000000..444a765 --- /dev/null +++ b/packages/icons/src/icons/empty-icon.vue @@ -0,0 +1,27 @@ + diff --git a/packages/icons/src/index.ts b/packages/icons/src/index.ts new file mode 100644 index 0000000..3ec38cd --- /dev/null +++ b/packages/icons/src/index.ts @@ -0,0 +1,5 @@ +export * from './iconify-offline/index.js'; +export * from './iconify/index.js'; +export { default as EmptyIcon } from './icons/empty-icon.vue'; +export * from './svg/index.js'; +export { VbenIcon } from '@vben-core/shadcn-ui'; diff --git a/packages/icons/src/svg/icons/antdv-logo.svg b/packages/icons/src/svg/icons/antdv-logo.svg new file mode 100644 index 0000000..dbfcee7 --- /dev/null +++ b/packages/icons/src/svg/icons/antdv-logo.svg @@ -0,0 +1,29 @@ + + + + Vue + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/avatar-1.svg b/packages/icons/src/svg/icons/avatar-1.svg new file mode 100644 index 0000000..b05970d --- /dev/null +++ b/packages/icons/src/svg/icons/avatar-1.svg @@ -0,0 +1 @@ +Asset 15 diff --git a/packages/icons/src/svg/icons/avatar-2.svg b/packages/icons/src/svg/icons/avatar-2.svg new file mode 100644 index 0000000..8e17b0b --- /dev/null +++ b/packages/icons/src/svg/icons/avatar-2.svg @@ -0,0 +1 @@ +Asset 16 diff --git a/packages/icons/src/svg/icons/avatar-3.svg b/packages/icons/src/svg/icons/avatar-3.svg new file mode 100644 index 0000000..ca79178 --- /dev/null +++ b/packages/icons/src/svg/icons/avatar-3.svg @@ -0,0 +1 @@ +Asset 17 diff --git a/packages/icons/src/svg/icons/avatar-4.svg b/packages/icons/src/svg/icons/avatar-4.svg new file mode 100644 index 0000000..d9138df --- /dev/null +++ b/packages/icons/src/svg/icons/avatar-4.svg @@ -0,0 +1 @@ +Asset 120 diff --git a/packages/icons/src/svg/icons/banlizhog.svg b/packages/icons/src/svg/icons/banlizhog.svg new file mode 100644 index 0000000..0b885e8 --- /dev/null +++ b/packages/icons/src/svg/icons/banlizhog.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/bell.svg b/packages/icons/src/svg/icons/bell.svg new file mode 100644 index 0000000..90c34e1 --- /dev/null +++ b/packages/icons/src/svg/icons/bell.svg @@ -0,0 +1 @@ +Asset 510 diff --git a/packages/icons/src/svg/icons/cake.svg b/packages/icons/src/svg/icons/cake.svg new file mode 100644 index 0000000..43901fb --- /dev/null +++ b/packages/icons/src/svg/icons/cake.svg @@ -0,0 +1 @@ +Asset 480% diff --git a/packages/icons/src/svg/icons/card.svg b/packages/icons/src/svg/icons/card.svg new file mode 100644 index 0000000..40ff3e3 --- /dev/null +++ b/packages/icons/src/svg/icons/card.svg @@ -0,0 +1 @@ + diff --git a/packages/icons/src/svg/icons/daichuli.svg b/packages/icons/src/svg/icons/daichuli.svg new file mode 100644 index 0000000..e16770b --- /dev/null +++ b/packages/icons/src/svg/icons/daichuli.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/daishenhe.svg b/packages/icons/src/svg/icons/daishenhe.svg new file mode 100644 index 0000000..ddaa5da --- /dev/null +++ b/packages/icons/src/svg/icons/daishenhe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/diaoyongcishu.svg b/packages/icons/src/svg/icons/diaoyongcishu.svg new file mode 100644 index 0000000..d64b161 --- /dev/null +++ b/packages/icons/src/svg/icons/diaoyongcishu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/download.svg b/packages/icons/src/svg/icons/download.svg new file mode 100644 index 0000000..af9ff15 --- /dev/null +++ b/packages/icons/src/svg/icons/download.svg @@ -0,0 +1 @@ + diff --git a/packages/icons/src/svg/icons/fankuidan.svg b/packages/icons/src/svg/icons/fankuidan.svg new file mode 100644 index 0000000..de48b3e --- /dev/null +++ b/packages/icons/src/svg/icons/fankuidan.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/jingzhongnengli.svg b/packages/icons/src/svg/icons/jingzhongnengli.svg new file mode 100644 index 0000000..6067d2a --- /dev/null +++ b/packages/icons/src/svg/icons/jingzhongnengli.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/jinjirenwu.svg b/packages/icons/src/svg/icons/jinjirenwu.svg new file mode 100644 index 0000000..b0056e5 --- /dev/null +++ b/packages/icons/src/svg/icons/jinjirenwu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/message.svg b/packages/icons/src/svg/icons/message.svg new file mode 100644 index 0000000..9bdbc90 --- /dev/null +++ b/packages/icons/src/svg/icons/message.svg @@ -0,0 +1 @@ + diff --git a/packages/icons/src/svg/icons/minganrenwu.svg b/packages/icons/src/svg/icons/minganrenwu.svg new file mode 100644 index 0000000..7521906 --- /dev/null +++ b/packages/icons/src/svg/icons/minganrenwu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/pingjunhaoshi.svg b/packages/icons/src/svg/icons/pingjunhaoshi.svg new file mode 100644 index 0000000..a81b915 --- /dev/null +++ b/packages/icons/src/svg/icons/pingjunhaoshi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/putongrenwu.svg b/packages/icons/src/svg/icons/putongrenwu.svg new file mode 100644 index 0000000..da5aacd --- /dev/null +++ b/packages/icons/src/svg/icons/putongrenwu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/qingzhirenwu.svg b/packages/icons/src/svg/icons/qingzhirenwu.svg new file mode 100644 index 0000000..230d283 --- /dev/null +++ b/packages/icons/src/svg/icons/qingzhirenwu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/teshurenwu.svg b/packages/icons/src/svg/icons/teshurenwu.svg new file mode 100644 index 0000000..b3ef1d5 --- /dev/null +++ b/packages/icons/src/svg/icons/teshurenwu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/wofaqide.svg b/packages/icons/src/svg/icons/wofaqide.svg new file mode 100644 index 0000000..6ded350 --- /dev/null +++ b/packages/icons/src/svg/icons/wofaqide.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/xiezuodan.svg b/packages/icons/src/svg/icons/xiezuodan.svg new file mode 100644 index 0000000..95b94e8 --- /dev/null +++ b/packages/icons/src/svg/icons/xiezuodan.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/yibanli.svg b/packages/icons/src/svg/icons/yibanli.svg new file mode 100644 index 0000000..d55abbd --- /dev/null +++ b/packages/icons/src/svg/icons/yibanli.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/yichuli.svg b/packages/icons/src/svg/icons/yichuli.svg new file mode 100644 index 0000000..aa4f9fb --- /dev/null +++ b/packages/icons/src/svg/icons/yichuli.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/yishenhe.svg b/packages/icons/src/svg/icons/yishenhe.svg new file mode 100644 index 0000000..a6ed62e --- /dev/null +++ b/packages/icons/src/svg/icons/yishenhe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/yishenpi.svg b/packages/icons/src/svg/icons/yishenpi.svg new file mode 100644 index 0000000..db02266 --- /dev/null +++ b/packages/icons/src/svg/icons/yishenpi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/zhilingrenwu.svg b/packages/icons/src/svg/icons/zhilingrenwu.svg new file mode 100644 index 0000000..3288333 --- /dev/null +++ b/packages/icons/src/svg/icons/zhilingrenwu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/index.ts b/packages/icons/src/svg/index.ts new file mode 100644 index 0000000..8a4bf49 --- /dev/null +++ b/packages/icons/src/svg/index.ts @@ -0,0 +1,72 @@ +/* + * @Author: hcc han_chen301@163.com + * @Date: 2025-05-23 09:27:20 + * @LastEditors: hcc + * @LastEditTime: 2025-06-06 14:34:02 + * @remarker: + */ +import { createIconifyIcon } from '@vben-core/icons'; + +import './load.js'; + +const SvgAvatar1Icon = createIconifyIcon('svg:avatar-1'); +const SvgAvatar2Icon = createIconifyIcon('svg:avatar-2'); +const SvgAvatar3Icon = createIconifyIcon('svg:avatar-3'); +const SvgAvatar4Icon = createIconifyIcon('svg:avatar-4'); +const SvgDownloadIcon = createIconifyIcon('svg:download'); +const SvgCardIcon = createIconifyIcon('svg:card'); +const SvgBellIcon = createIconifyIcon('svg:bell'); +const SvgCakeIcon = createIconifyIcon('svg:cake'); +const SvgAntdvLogoIcon = createIconifyIcon('svg:antdv-logo'); + +const SvgBlzIcon = createIconifyIcon('svg:banlizhong'); +const SvgDclIcon = createIconifyIcon('svg:daichuli'); +const SvgDshIcon = createIconifyIcon('svg:daishenhe'); +const SvgDycsIcon = createIconifyIcon('svg:diaoyongcishu'); +const SvgFkdIcon = createIconifyIcon('svg:fankuidan'); +const SvgMgrwIcon = createIconifyIcon('svg:minganrenwu'); +const SvgJznlIcon = createIconifyIcon('svg:jingzhongnengli'); +const SvgJjrwIcon = createIconifyIcon('svg:jinjirenwu'); +const SvgPjhsIcon = createIconifyIcon('svg:pingjunhaoshi'); +const SvgPtrwIcon = createIconifyIcon('svg:putongrenwu'); +const SvgQzrwIcon = createIconifyIcon('svg:qingzhirenwu'); +const SvgTsrwIcon = createIconifyIcon('svg:teshurenwu'); +const SvgWfqdIcon = createIconifyIcon('svg:wofaqide'); +const SvgXzdIcon = createIconifyIcon('svg:xiezuodan'); +const SvgYBlIcon = createIconifyIcon('svg:yibanli'); +const SvgYclIcon = createIconifyIcon('svg:yichuli'); +const SvgYshIcon = createIconifyIcon('svg:yishenhe'); +const SvgYspIcon = createIconifyIcon('svg:yishenpi'); +const SvgZlrwIcon = createIconifyIcon('svg:zhilingrenwu'); +export { + SvgAntdvLogoIcon, + SvgAvatar1Icon, + SvgAvatar2Icon, + SvgAvatar3Icon, + SvgAvatar4Icon, + SvgBellIcon, + SvgBlzIcon, + SvgCakeIcon, + SvgCardIcon, + SvgDclIcon, + SvgDownloadIcon, + SvgDshIcon, + SvgDycsIcon, + SvgFkdIcon, + SvgJjrwIcon, + SvgJznlIcon, + SvgMgrwIcon, + SvgPjhsIcon, + SvgPtrwIcon, + SvgQzrwIcon, + SvgTsrwIcon, + SvgWfqdIcon, + SvgXzdIcon, + SvgYBlIcon, + SvgYclIcon, + SvgYshIcon, + SvgYspIcon, + SvgZlrwIcon, +}; + +export { default as SvgMessageUrl } from './icons/message.svg'; diff --git a/packages/icons/src/svg/load.ts b/packages/icons/src/svg/load.ts new file mode 100644 index 0000000..25f4261 --- /dev/null +++ b/packages/icons/src/svg/load.ts @@ -0,0 +1,61 @@ +import type { IconifyIconStructure } from '@vben-core/icons'; + +import { addIcon } from '@vben-core/icons'; + +let loaded = false; +if (!loaded) { + loadSvgIcons(); + loaded = true; +} + +function parseSvg(svgData: string): IconifyIconStructure { + const parser = new DOMParser(); + const xmlDoc = parser.parseFromString(svgData, 'image/svg+xml'); + const svgElement = xmlDoc.documentElement; + + const svgContent = [...svgElement.childNodes] + .filter((node) => node.nodeType === Node.ELEMENT_NODE) + .map((node) => new XMLSerializer().serializeToString(node)) + .join(''); + + const viewBoxValue = svgElement.getAttribute('viewBox') || ''; + const [left, top, width, height] = viewBoxValue.split(' ').map((val) => { + const num = Number(val); + return Number.isNaN(num) ? undefined : num; + }); + + return { + body: svgContent, + height, + left, + top, + width, + }; +} + +/** + * 自定义的svg图片转化为组件 + * @example ./svg/avatar.svg + * + */ +async function loadSvgIcons() { + const svgEagers = import.meta.glob('./icons/**', { + eager: true, + query: '?raw', + }); + + await Promise.all( + Object.entries(svgEagers).map((svg) => { + const [key, body] = svg as [string, { default: string } | string]; + + // ./icons/xxxx.svg => xxxxxx + const start = key.lastIndexOf('/') + 1; + const end = key.lastIndexOf('.'); + const iconName = key.slice(start, end); + + return addIcon(`svg:${iconName}`, { + ...parseSvg(typeof body === 'object' ? body.default : body), + }); + }), + ); +} diff --git a/packages/icons/tsconfig.json b/packages/icons/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/packages/icons/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/locales/package.json b/packages/locales/package.json new file mode 100644 index 0000000..4ec6430 --- /dev/null +++ b/packages/locales/package.json @@ -0,0 +1,28 @@ +{ + "name": "@vben/locales", + "version": "5.5.3", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/locales" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@intlify/core-base": "catalog:", + "@vben-core/composables": "workspace:*", + "vue": "catalog:", + "vue-i18n": "catalog:" + } +} diff --git a/packages/locales/src/i18n.ts b/packages/locales/src/i18n.ts new file mode 100644 index 0000000..90ae813 --- /dev/null +++ b/packages/locales/src/i18n.ts @@ -0,0 +1,146 @@ +import type { App } from 'vue'; +import type { Locale } from 'vue-i18n'; + +import type { + ImportLocaleFn, + LoadMessageFn, + LocaleSetupOptions, + SupportedLanguagesType, +} from './typing'; + +import { useSimpleLocale } from '@vben-core/composables'; +import { unref } from 'vue'; +import { createI18n } from 'vue-i18n'; + +const i18n = createI18n({ + globalInjection: true, + legacy: false, + locale: '', + messages: {}, +}); + +const modules = import.meta.glob('./langs/**/*.json'); + +const { setSimpleLocale } = useSimpleLocale(); + +const localesMap = loadLocalesMapFromDir( + /\.\/langs\/([^/]+)\/(.*)\.json$/, + modules, +); +let loadMessages: LoadMessageFn; + +/** + * Load locale modules + * @param modules + */ +function loadLocalesMap(modules: Record Promise>) { + const localesMap: Record = {}; + + for (const [path, loadLocale] of Object.entries(modules)) { + const key = path.match(/([\w-]*)\.(json)/)?.[1]; + if (key) { + localesMap[key] = loadLocale as ImportLocaleFn; + } + } + return localesMap; +} + +/** + * Load locale modules with directory structure + * @param regexp - Regular expression to match language and file names + * @param modules - The modules object containing paths and import functions + * @returns A map of locales to their corresponding import functions + */ +function loadLocalesMapFromDir( + regexp: RegExp, + modules: Record Promise>, +): Record { + const localesRaw: Record Promise>> = {}; + const localesMap: Record = {}; + + // Iterate over the modules to extract language and file names + for (const path in modules) { + const match = path.match(regexp); + if (match) { + const [_, locale, fileName] = match; + if (locale && fileName) { + if (!localesRaw[locale]) { + localesRaw[locale] = {}; + } + if (modules[path]) { + localesRaw[locale][fileName] = modules[path]; + } + } + } + } + + // Convert raw locale data into async import functions + for (const [locale, files] of Object.entries(localesRaw)) { + localesMap[locale] = async () => { + const messages: Record = {}; + for (const [fileName, importFn] of Object.entries(files)) { + messages[fileName] = ((await importFn()) as any)?.default; + } + return { default: messages }; + }; + } + + return localesMap; +} + +/** + * Set i18n language + * @param locale + */ +function setI18nLanguage(locale: Locale) { + i18n.global.locale.value = locale; + + document?.querySelector('html')?.setAttribute('lang', locale); +} + +async function setupI18n(app: App, options: LocaleSetupOptions = {}) { + const { defaultLocale = 'zh-CN' } = options; + // app可以自行扩展一些第三方库和组件库的国际化 + loadMessages = options.loadMessages || (async () => ({})); + app.use(i18n); + await loadLocaleMessages(defaultLocale); + + // 在控制台打印警告 + i18n.global.setMissingHandler((locale, key) => { + if (options.missingWarn && key.includes('.')) { + console.warn( + `[intlify] Not found '${key}' key in '${locale}' locale messages.`, + ); + } + }); +} + +/** + * Load locale messages + * @param lang + */ +async function loadLocaleMessages(lang: SupportedLanguagesType) { + if (unref(i18n.global.locale) === lang) { + return setI18nLanguage(lang); + } + setSimpleLocale(lang); + + const message = await localesMap[lang]?.(); + + if (message?.default) { + i18n.global.setLocaleMessage(lang, message.default); + } + + const mergeMessage = await loadMessages(lang); + i18n.global.mergeLocaleMessage(lang, mergeMessage); + + return setI18nLanguage(lang); +} + +export { + i18n, + loadLocaleMessages, + loadLocalesMap, + loadLocalesMapFromDir, + setupI18n, +}; diff --git a/packages/locales/src/index.ts b/packages/locales/src/index.ts new file mode 100644 index 0000000..eb2251a --- /dev/null +++ b/packages/locales/src/index.ts @@ -0,0 +1,28 @@ +import { + i18n, + loadLocaleMessages, + loadLocalesMap, + loadLocalesMapFromDir, + setupI18n, +} from './i18n'; + +const $t = i18n.global.t; + +export { + $t, + i18n, + loadLocaleMessages, + loadLocalesMap, + loadLocalesMapFromDir, + setupI18n, +}; +export { + type ImportLocaleFn, + type LocaleSetupOptions, + type SupportedLanguagesType, +} from './typing'; +export type { CompileError } from '@intlify/core-base'; + +export { useI18n } from 'vue-i18n'; + +export type { Locale } from 'vue-i18n'; diff --git a/packages/locales/src/langs/en-US/authentication.json b/packages/locales/src/langs/en-US/authentication.json new file mode 100644 index 0000000..f294cdd --- /dev/null +++ b/packages/locales/src/langs/en-US/authentication.json @@ -0,0 +1,56 @@ +{ + "welcomeBack": "Welcome Back", + "pageTitle": "Plug-and-play Admin system", + "pageDesc": "Efficient, versatile frontend template", + "loginSuccess": "Login Successful", + "loginSuccessDesc": "Welcome Back", + "loginSubtitle": "Enter your account details to manage your projects", + "selectAccount": "Quick Select Account", + "username": "Username", + "password": "Password", + "usernameTip": "Please enter username", + "passwordErrorTip": "Password is incorrect", + "passwordTip": "Please enter password", + "verifyRequiredTip": "Please complete the verification first", + "rememberMe": "Remember Me", + "createAnAccount": "Create an Account", + "createAccount": "Create Account", + "alreadyHaveAccount": "Already have an account?", + "accountTip": "Don't have an account?", + "signUp": "Sign Up", + "signUpSubtitle": "Make managing your applications simple and fun", + "confirmPassword": "Confirm Password", + "confirmPasswordTip": "The passwords do not match", + "agree": "I agree to", + "privacyPolicy": "Privacy-policy", + "terms": "Terms", + "agreeTip": "Please agree to the Privacy Policy and Terms", + "goToLogin": "Login instead", + "passwordStrength": "Use 8 or more characters with a mix of letters, numbers & symbols", + "forgetPassword": "Forget Password?", + "forgetPasswordSubtitle": "Enter your email and we'll send you instructions to reset your password", + "emailTip": "Please enter email", + "emailValidErrorTip": "The email format you entered is incorrect", + "sendResetLink": "Send Reset Link", + "email": "Email", + "qrcodeSubtitle": "Scan the QR code with your phone to login", + "qrcodePrompt": "Click 'Confirm' after scanning to complete login", + "qrcodeLogin": "QR Code Login", + "codeSubtitle": "Enter your phone number to start managing your project", + "code": "Security code", + "codeTip": "Security code required {0} characters", + "mobile": "Mobile", + "mobileLogin": "Mobile Login", + "mobileTip": "Please enter mobile number", + "mobileErrortip": "The phone number format is incorrect", + "sendCode": "Get Security code", + "sendText": "Resend in {0}s", + "thirdPartyLogin": "Or continue with", + "loginAgainTitle": "Please Log In Again", + "loginAgainSubTitle": "Your login session has expired. Please log in again to continue.", + "layout": { + "center": "Align Center", + "alignLeft": "Align Left", + "alignRight": "Align Right" + } +} diff --git a/packages/locales/src/langs/en-US/common.json b/packages/locales/src/langs/en-US/common.json new file mode 100644 index 0000000..01fc9ce --- /dev/null +++ b/packages/locales/src/langs/en-US/common.json @@ -0,0 +1,14 @@ +{ + "back": "Back", + "backToHome": "Back To Home", + "login": "Login", + "logout": "Logout", + "prompt": "Prompt", + "cancel": "Cancel", + "confirm": "Confirm", + "noData": "No Data", + "refresh": "Refresh", + "loadingMenu": "Loading Menu", + "query": "Search", + "search": "Search" +} diff --git a/packages/locales/src/langs/en-US/preferences.json b/packages/locales/src/langs/en-US/preferences.json new file mode 100644 index 0000000..029a9a5 --- /dev/null +++ b/packages/locales/src/langs/en-US/preferences.json @@ -0,0 +1,186 @@ +{ + "title": "Preferences", + "subtitle": "Customize Preferences & Preview in Real Time", + "resetTip": "Data has changed, click to reset", + "resetTitle": "Reset Preferences", + "resetSuccess": "Preferences reset successfully", + "appearance": "Appearance", + "layout": "Layout", + "content": "Content", + "other": "Other", + "wide": "Wide", + "compact": "Fixed", + "followSystem": "Follow System", + "vertical": "Vertical", + "verticalTip": "Side vertical menu mode", + "horizontal": "Horizontal", + "horizontalTip": "Horizontal menu mode, all menus displayed at the top", + "twoColumn": "Two Column", + "twoColumnTip": "Vertical Two Column Menu Mode", + "headerSidebarNav": "Header Vertical", + "headerSidebarNavTip": "Header Full Width, Sidebar Navigation Mode", + "headerTwoColumn": "Header Two Column", + "headerTwoColumnTip": "Header Navigation & Sidebar Two Column co-exists", + "mixedMenu": "Mixed Menu", + "mixedMenuTip": "Vertical & Horizontal Menu Co-exists", + "fullContent": "Full Content", + "fullContentTip": "Only display content body, hide all menus", + "normal": "Normal", + "plain": "Plain", + "rounded": "Rounded", + "copyPreferences": "Copy Preferences", + "copyPreferencesSuccessTitle": "Copy successful", + "copyPreferencesSuccess": "Copy successful, please override in `src/preferences.ts` under app", + "clearAndLogout": "Clear Cache & Logout", + "mode": "Mode", + "general": "General", + "language": "Language", + "dynamicTitle": "Dynamic Title", + "watermark": "Watermark", + "checkUpdates": "Periodic update check", + "position": { + "title": "Preferences Postion", + "header": "Header", + "auto": "Auto", + "fixed": "Fixed" + }, + "sidebar": { + "title": "Sidebar", + "width": "Width", + "visible": "Show Sidebar", + "collapsed": "Collpase Menu", + "collapsedShowTitle": "Show Menu Title", + "autoActivateChild": "Auto Activate SubMenu", + "autoActivateChildTip": "`Enabled` to automatically activate the submenu while click menu.", + "expandOnHover": "Expand On Hover", + "expandOnHoverTip": "When the mouse hovers over menu, \n `Enabled` to expand children menus \n `Disabled` to expand whole sidebar." + }, + "tabbar": { + "title": "Tabbar", + "enable": "Enable Tab Bar", + "icon": "Show Tabbar Icon", + "showMore": "Show More Button", + "showMaximize": "Show Maximize Button", + "persist": "Persist Tabs", + "maxCount": "Max Count of Tabs", + "maxCountTip": "When the number of tabs exceeds the maximum,\nthe oldest tab will be closed.\n Set to 0 to disable count checking.", + "draggable": "Enable Draggable Sort", + "wheelable": "Support Mouse Wheel", + "middleClickClose": "Close Tab when Mouse Middle Button Click", + "wheelableTip": "When enabled, the Tabbar area responds to vertical scrolling events of the scroll wheel.", + "styleType": { + "title": "Tabs Style", + "chrome": "Chrome", + "card": "Card", + "plain": "Plain", + "brisk": "Brisk" + }, + "contextMenu": { + "reload": "Reload", + "close": "Close", + "pin": "Pin", + "unpin": "Unpin", + "closeLeft": "Close Left Tabs", + "closeRight": "Close Right Tabs", + "closeOther": "Close Other Tabs", + "closeAll": "Close All Tabs", + "openInNewWindow": "Open in New Window", + "maximize": "Maximize", + "restoreMaximize": "Restore" + } + }, + "navigationMenu": { + "title": "Navigation Menu", + "style": "Navigation Menu Style", + "accordion": "Sidebar Accordion Menu", + "split": "Navigation Menu Separation", + "splitTip": "When enabled, the sidebar displays the top bar's submenu" + }, + "breadcrumb": { + "title": "Breadcrumb", + "home": "Show Home Button", + "enable": "Enable Breadcrumb", + "icon": "Show Breadcrumb Icon", + "background": "background", + "style": "Breadcrumb Style", + "hideOnlyOne": "Hidden when only one" + }, + "animation": { + "title": "Animation", + "loading": "Page Loading", + "transition": "Page Transition", + "progress": "Page Progress" + }, + "theme": { + "title": "Theme", + "radius": "Radius", + "light": "Light", + "dark": "Dark", + "darkSidebar": "Semi Dark Sidebar", + "darkHeader": "Semi Dark Header", + "weakMode": "Weak Mode", + "grayMode": "Gray Mode", + "builtin": { + "title": "Built-in", + "default": "Default", + "violet": "Violet", + "pink": "Pink", + "rose": "Rose", + "skyBlue": "Sky Blue", + "deepBlue": "Deep Blue", + "green": "Green", + "deepGreen": "Deep Green", + "orange": "Orange", + "yellow": "Yellow", + "zinc": "Zinc", + "neutral": "Neutral", + "slate": "Slate", + "gray": "Gray", + "custom": "Custom" + } + }, + "header": { + "title": "Header", + "visible": "Show Header", + "modeStatic": "Static", + "modeFixed": "Fixed", + "modeAuto": "Auto hide & Show", + "modeAutoScroll": "Scroll to Hide & Show", + "menuAlign": "Menu Align", + "menuAlignStart": "Start", + "menuAlignEnd": "End", + "menuAlignCenter": "Center" + }, + "footer": { + "title": "Footer", + "visible": "Show Footer", + "fixed": "Fixed at Bottom" + }, + "copyright": { + "title": "Copyright", + "enable": "Enable Copyright", + "companyName": "Company Name", + "companySiteLink": "Company Site Link", + "date": "Date", + "icp": "ICP License Number", + "icpLink": "ICP Site Link" + }, + "shortcutKeys": { + "title": "Shortcut Keys", + "global": "Global", + "search": "Global Search", + "logout": "Logout", + "preferences": "Preferences" + }, + "widget": { + "title": "Widget", + "globalSearch": "Enable Global Search", + "fullscreen": "Enable Fullscreen", + "themeToggle": "Enable Theme Toggle", + "languageToggle": "Enable Language Toggle", + "notification": "Enable Notification", + "sidebarToggle": "Enable Sidebar Toggle", + "lockScreen": "Enable Lock Screen", + "refresh": "Enable Refresh" + } +} diff --git a/packages/locales/src/langs/en-US/ui.json b/packages/locales/src/langs/en-US/ui.json new file mode 100644 index 0000000..d2a3d1c --- /dev/null +++ b/packages/locales/src/langs/en-US/ui.json @@ -0,0 +1,82 @@ +{ + "formRules": { + "required": "Please enter {0}", + "selectRequired": "Please select {0}" + }, + "placeholder": { + "input": "Please enter", + "select": "Please select" + }, + "captcha": { + "title": "Please complete the security verification", + "sliderSuccessText": "Passed", + "sliderDefaultText": "Slider and drag", + "alt": "Supports img tag src attribute value", + "sliderRotateDefaultTip": "Click picture to refresh", + "sliderRotateFailTip": "Validation failed", + "sliderRotateSuccessTip": "Validation successful, time {0} seconds", + "refreshAriaLabel": "Refresh captcha", + "confirmAriaLabel": "Confirm selection", + "confirm": "Confirm", + "pointAriaLabel": "Click point", + "clickInOrder": "Please click in order" + }, + "iconPicker": { + "placeholder": "Select an icon", + "search": "Search icon..." + }, + "fallback": { + "pageNotFound": "Oops! Page Not Found", + "pageNotFoundDesc": "Sorry, we couldn't find the page you were looking for.", + "forbidden": "Oops! Access Denied", + "forbiddenDesc": "Sorry, but you don't have permission to access this page.", + "internalError": "Oops! Something Went Wrong", + "internalErrorDesc": "Sorry, but the server encountered an error.", + "offline": "Offline Page", + "offlineError": "Oops! Network Error", + "offlineErrorDesc": "Sorry, can't connect to the internet. Check your connection.", + "comingSoon": "Coming Soon", + "http": { + "requestTimeout": "The request timed out. Please try again later.", + "networkError": "A network error occurred. Please check your internet connection and try again.", + "badRequest": "Bad Request. Please check your input and try again.", + "unauthorized": "Unauthorized. Please log in to continue.", + "forbidden": "Forbidden. You do not have permission to access this resource.", + "notFound": "Not Found. The requested resource could not be found.", + "internalServerError": "Internal Server Error. Something went wrong on our end. Please try again later." + } + }, + "widgets": { + "document": "Document", + "profile": "Profile", + "qa": "Q&A", + "setting": "Settings", + "logoutTip": "Do you want to logout?", + "viewAll": "View All Messages", + "notifications": "Notifications", + "markAllAsRead": "Make All as Read", + "clearNotifications": "Clear", + "checkUpdatesTitle": "New Version Available", + "checkUpdatesDescription": "Click to refresh and get the latest version", + "search": { + "title": "Search", + "searchNavigate": "Search Navigation", + "select": "Select", + "navigate": "Navigate", + "close": "Close", + "noResults": "No Search Results Found", + "noRecent": "No Search History", + "recent": "Search History" + }, + "lockScreen": { + "title": "Lock Screen", + "screenButton": "Locking", + "password": "Password", + "placeholder": "Please enter password", + "unlock": "Click to unlock", + "errorPasswordTip": "Password error, please re-enter", + "backToLogin": "Back to login", + "entry": "Enter the system" + } + } +} diff --git a/packages/locales/src/langs/zh-CN/authentication.json b/packages/locales/src/langs/zh-CN/authentication.json new file mode 100644 index 0000000..21875a3 --- /dev/null +++ b/packages/locales/src/langs/zh-CN/authentication.json @@ -0,0 +1,56 @@ +{ + "welcomeBack": "欢迎回来", + "pageTitle": "欢迎使用合成作战指挥调度系统", + "pageDesc": "工程化、高性能", + "loginSuccess": "登录成功", + "loginSuccessDesc": "欢迎回来", + "loginSubtitle": "请输入您的帐户信息以开始进行使用", + "selectAccount": "快速选择账号", + "username": "账号", + "password": "密码", + "usernameTip": "请输入用户名", + "passwordTip": "请输入密码", + "verifyRequiredTip": "请先完成验证", + "passwordErrorTip": "密码错误", + "rememberMe": "记住账号", + "createAnAccount": "创建一个账号", + "createAccount": "创建账号", + "alreadyHaveAccount": "已经有账号了?", + "accountTip": "还没有账号?", + "signUp": "注册", + "signUpSubtitle": "让您的应用程序管理变得简单而有趣", + "confirmPassword": "确认密码", + "confirmPasswordTip": "两次输入的密码不一致", + "agree": "我同意", + "privacyPolicy": "隐私政策", + "terms": "条款", + "agreeTip": "请同意隐私政策和条款", + "goToLogin": "去登录", + "passwordStrength": "使用 8 个或更多字符,混合字母、数字和符号", + "forgetPassword": "忘记密码?", + "forgetPasswordSubtitle": "输入您的电子邮件,我们将向您发送重置密码的连接", + "emailTip": "请输入邮箱", + "emailValidErrorTip": "你输入的邮箱格式不正确", + "sendResetLink": "发送重置链接", + "email": "邮箱", + "qrcodeSubtitle": "请用手机扫描二维码登录", + "qrcodePrompt": "扫码后点击 '确认',即可完成登录", + "qrcodeLogin": "扫码登录", + "codeSubtitle": "请输入您的手机号码以开始管理您的项目", + "code": "验证码", + "codeTip": "请输入{0}位验证码", + "mobile": "手机号码", + "mobileTip": "请输入手机号", + "mobileErrortip": "手机号码格式错误", + "mobileLogin": "手机号登录", + "sendCode": "获取验证码", + "sendText": "{0}秒后重新获取", + "thirdPartyLogin": "其他登录方式", + "loginAgainTitle": "重新登录", + "loginAgainSubTitle": "您的登录状态已过期,请重新登录以继续。", + "layout": { + "center": "居中", + "alignLeft": "居左", + "alignRight": "居右" + } +} diff --git a/packages/locales/src/langs/zh-CN/common.json b/packages/locales/src/langs/zh-CN/common.json new file mode 100644 index 0000000..d0b5ffc --- /dev/null +++ b/packages/locales/src/langs/zh-CN/common.json @@ -0,0 +1,14 @@ +{ + "back": "返回", + "backToHome": "返回首页", + "login": "登录", + "logout": "退出登录", + "prompt": "提示", + "cancel": "取消", + "confirm": "确认", + "noData": "暂无数据", + "refresh": "刷新", + "loadingMenu": "加载菜单中", + "query": "查询", + "search": "搜索" +} diff --git a/packages/locales/src/langs/zh-CN/preferences.json b/packages/locales/src/langs/zh-CN/preferences.json new file mode 100644 index 0000000..fde3368 --- /dev/null +++ b/packages/locales/src/langs/zh-CN/preferences.json @@ -0,0 +1,186 @@ +{ + "title": "偏好设置", + "subtitle": "自定义偏好设置 & 实时预览", + "resetTitle": "重置偏好设置", + "resetTip": "数据有变化,点击可进行重置", + "resetSuccess": "重置偏好设置成功", + "appearance": "外观", + "layout": "布局", + "content": "内容", + "other": "其它", + "wide": "流式", + "compact": "定宽", + "followSystem": "跟随系统", + "vertical": "垂直", + "verticalTip": "侧边垂直菜单模式", + "horizontal": "水平", + "horizontalTip": "水平菜单模式,菜单全部显示在顶部", + "twoColumn": "双列菜单", + "twoColumnTip": "垂直双列菜单模式", + "headerSidebarNav": "侧边导航", + "headerSidebarNavTip": "顶部通栏,侧边导航模式", + "headerTwoColumn": "混合双列", + "headerTwoColumnTip": "双列、水平菜单共存模式", + "mixedMenu": "混合垂直", + "mixedMenuTip": "垂直水平菜单共存", + "fullContent": "内容全屏", + "fullContentTip": "不显示任何菜单,只显示内容主体", + "normal": "常规", + "plain": "朴素", + "rounded": "圆润", + "copyPreferences": "复制偏好设置", + "copyPreferencesSuccessTitle": "复制成功", + "copyPreferencesSuccess": "复制成功,请在 app 下的 `src/preferences.ts`内进行覆盖", + "clearAndLogout": "清空缓存 & 退出登录", + "mode": "模式", + "general": "通用", + "language": "语言", + "dynamicTitle": "动态标题", + "watermark": "水印", + "checkUpdates": "定时检查更新", + "position": { + "title": "偏好设置位置", + "header": "顶栏", + "auto": "自动", + "fixed": "固定" + }, + "sidebar": { + "title": "侧边栏", + "width": "宽度", + "visible": "显示侧边栏", + "collapsed": "折叠菜单", + "collapsedShowTitle": "折叠显示菜单名", + "autoActivateChild": "自动激活子菜单", + "autoActivateChildTip": "点击顶层菜单时,自动激活第一个子菜单或者上一次激活的子菜单", + "expandOnHover": "鼠标悬停展开", + "expandOnHoverTip": "鼠标在折叠区域悬浮时,`启用`则展开当前子菜单,`禁用`则展开整个侧边栏" + }, + "tabbar": { + "title": "标签栏", + "enable": "启用标签栏", + "icon": "显示标签栏图标", + "showMore": "显示更多按钮", + "showMaximize": "显示最大化按钮", + "persist": "持久化标签页", + "maxCount": "最大标签数", + "maxCountTip": "每次打开新的标签时如果超过最大标签数,\n会自动关闭一个最先打开的标签\n设置为 0 则不限制", + "draggable": "启动拖拽排序", + "wheelable": "启用纵向滚轮响应", + "middleClickClose": "点击鼠标中键关闭标签页", + "wheelableTip": "开启后,标签栏区域可以响应滚轮的纵向滚动事件。\n关闭时,只能响应系统的横向滚动事件(需要按下Shift再滚动滚轮)", + "styleType": { + "title": "标签页风格", + "chrome": "谷歌", + "card": "卡片", + "plain": "朴素", + "brisk": "轻快" + }, + "contextMenu": { + "reload": "重新加载", + "close": "关闭", + "pin": "固定", + "unpin": "取消固定", + "closeLeft": "关闭左侧标签页", + "closeRight": "关闭右侧标签页", + "closeOther": "关闭其它标签页", + "closeAll": "关闭全部标签页", + "openInNewWindow": "在新窗口打开", + "maximize": "最大化", + "restoreMaximize": "还原" + } + }, + "navigationMenu": { + "title": "导航菜单", + "style": "导航菜单风格", + "accordion": "侧边导航菜单手风琴模式", + "split": "导航菜单分离", + "splitTip": "开启时,侧边栏显示顶栏对应菜单的子菜单" + }, + "breadcrumb": { + "title": "面包屑导航", + "enable": "开启面包屑导航", + "icon": "显示面包屑图标", + "home": "显示首页按钮", + "style": "面包屑风格", + "hideOnlyOne": "仅有一个时隐藏", + "background": "背景" + }, + "animation": { + "title": "动画", + "loading": "页面切换 Loading", + "transition": "页面切换动画", + "progress": "页面切换进度条" + }, + "theme": { + "title": "主题", + "radius": "圆角", + "light": "浅色", + "dark": "深色", + "darkSidebar": "深色侧边栏", + "darkHeader": "深色顶栏", + "weakMode": "色弱模式", + "grayMode": "灰色模式", + "builtin": { + "title": "内置主题", + "default": "默认", + "violet": "紫罗兰", + "pink": "樱花粉", + "rose": "玫瑰红", + "skyBlue": "天蓝色", + "deepBlue": "深蓝色", + "green": "浅绿色", + "deepGreen": "深绿色", + "orange": "橙黄色", + "yellow": "柠檬黄", + "zinc": "锌色灰", + "neutral": "中性色", + "slate": "石板灰", + "gray": "中灰色", + "custom": "自定义" + } + }, + "header": { + "title": "顶栏", + "modeStatic": "静止", + "modeFixed": "固定", + "modeAuto": "自动隐藏和显示", + "modeAutoScroll": "滚动隐藏和显示", + "visible": "显示顶栏", + "menuAlign": "菜单位置", + "menuAlignStart": "左侧", + "menuAlignEnd": "右侧", + "menuAlignCenter": "居中" + }, + "footer": { + "title": "底栏", + "visible": "显示底栏", + "fixed": "固定在底部" + }, + "copyright": { + "title": "版权", + "enable": "启用版权", + "companyName": "公司名", + "companySiteLink": "公司主页", + "date": "日期", + "icp": "ICP 备案号", + "icpLink": "ICP 网站链接" + }, + "shortcutKeys": { + "title": "快捷键", + "global": "全局", + "search": "全局搜索", + "logout": "退出登录", + "preferences": "偏好设置" + }, + "widget": { + "title": "小部件", + "globalSearch": "启用全局搜索", + "fullscreen": "启用全屏", + "themeToggle": "启用主题切换", + "languageToggle": "启用语言切换", + "notification": "启用通知", + "sidebarToggle": "启用侧边栏切换", + "lockScreen": "启用锁屏", + "refresh": "启用刷新" + } +} diff --git a/packages/locales/src/langs/zh-CN/ui.json b/packages/locales/src/langs/zh-CN/ui.json new file mode 100644 index 0000000..1d30d1d --- /dev/null +++ b/packages/locales/src/langs/zh-CN/ui.json @@ -0,0 +1,82 @@ +{ + "formRules": { + "required": "请输入{0}", + "selectRequired": "请选择{0}" + }, + "placeholder": { + "input": "请输入", + "select": "请选择" + }, + "captcha": { + "title": "请完成安全验证", + "sliderSuccessText": "验证通过", + "sliderDefaultText": "请按住滑块拖动", + "sliderRotateDefaultTip": "点击图片可刷新", + "sliderRotateFailTip": "验证失败", + "sliderRotateSuccessTip": "验证成功,耗时{0}秒", + "alt": "支持img标签src属性值", + "refreshAriaLabel": "刷新验证码", + "confirmAriaLabel": "确认选择", + "confirm": "确认", + "pointAriaLabel": "点击点", + "clickInOrder": "请依次点击" + }, + "iconPicker": { + "placeholder": "选择一个图标", + "search": "搜索图标..." + }, + "fallback": { + "pageNotFound": "哎呀!未找到页面", + "pageNotFoundDesc": "抱歉,我们无法找到您要找的页面。", + "forbidden": "哎呀!访问被拒绝", + "forbiddenDesc": "抱歉,您没有权限访问此页面。", + "internalError": "哎呀!出错了", + "internalErrorDesc": "抱歉,服务器遇到错误。", + "offline": "离线页面", + "offlineError": "哎呀!网络错误", + "offlineErrorDesc": "抱歉,无法连接到互联网,请检查您的网络连接并重试。", + "comingSoon": "即将推出", + "http": { + "requestTimeout": "请求超时,请稍后再试。", + "networkError": "网络异常,请检查您的网络连接后重试。", + "badRequest": "请求错误。请检查您的输入并重试。", + "unauthorized": "登录认证过期,请重新登录后继续。", + "forbidden": "禁止访问, 您没有权限访问此资源。", + "notFound": "未找到, 请求的资源不存在。", + "internalServerError": "内部服务器错误,请稍后再试。" + } + }, + "widgets": { + "document": "文档", + "profile": "个人中心", + "qa": "问题 & 帮助", + "setting": "设置", + "logoutTip": "是否退出登录?", + "viewAll": "查看所有消息", + "notifications": "通知", + "markAllAsRead": "全部标记为已读", + "clearNotifications": "清空", + "checkUpdatesTitle": "新版本可用", + "checkUpdatesDescription": "点击刷新以获取最新版本", + "search": { + "title": "搜索", + "searchNavigate": "搜索导航菜单", + "select": "选择", + "navigate": "导航", + "close": "关闭", + "noResults": "未找到搜索结果", + "noRecent": "没有搜索历史", + "recent": "搜索历史" + }, + "lockScreen": { + "title": "锁定屏幕", + "screenButton": "锁定", + "password": "密码", + "placeholder": "请输入锁屏密码", + "unlock": "点击解锁", + "errorPasswordTip": "密码错误,请重新输入", + "backToLogin": "返回登录", + "entry": "进入系统" + } + } +} diff --git a/packages/locales/src/typing.ts b/packages/locales/src/typing.ts new file mode 100644 index 0000000..fdebac2 --- /dev/null +++ b/packages/locales/src/typing.ts @@ -0,0 +1,25 @@ +export type SupportedLanguagesType = 'en-US' | 'zh-CN'; + +export type ImportLocaleFn = () => Promise<{ default: Record }>; + +export type LoadMessageFn = ( + lang: SupportedLanguagesType, +) => Promise | undefined>; + +export interface LocaleSetupOptions { + /** + * Default language + * @default zh-CN + */ + defaultLocale?: SupportedLanguagesType; + /** + * Load message function + * @param lang + * @returns + */ + loadMessages?: LoadMessageFn; + /** + * Whether to warn when the key is not found + */ + missingWarn?: boolean; +} diff --git a/packages/locales/tsconfig.json b/packages/locales/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/packages/locales/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/preferences/package.json b/packages/preferences/package.json new file mode 100644 index 0000000..950ba84 --- /dev/null +++ b/packages/preferences/package.json @@ -0,0 +1,26 @@ +{ + "name": "@vben/preferences", + "version": "5.5.3", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/preferences" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben-core/preferences": "workspace:*", + "@vben-core/typings": "workspace:*" + } +} diff --git a/packages/preferences/src/index.ts b/packages/preferences/src/index.ts new file mode 100644 index 0000000..75fab43 --- /dev/null +++ b/packages/preferences/src/index.ts @@ -0,0 +1,17 @@ +import type { Preferences } from '@vben-core/preferences'; +import type { DeepPartial } from '@vben-core/typings'; + +/** + * 如果你想所有的app都使用相同的默认偏好设置,你可以在这里定义 + * 而不是去修改 @vben-core/preferences 中的默认偏好设置 + * @param preferences + * @returns + */ + +function defineOverridesPreferences(preferences: DeepPartial) { + return preferences; +} + +export { defineOverridesPreferences }; + +export * from '@vben-core/preferences'; diff --git a/packages/preferences/tsconfig.json b/packages/preferences/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/packages/preferences/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/stores/package.json b/packages/stores/package.json new file mode 100644 index 0000000..fe79f8e --- /dev/null +++ b/packages/stores/package.json @@ -0,0 +1,31 @@ +{ + "name": "@vben/stores", + "version": "5.5.3", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/stores" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben-core/preferences": "workspace:*", + "@vben-core/shared": "workspace:*", + "@vben-core/typings": "workspace:*", + "pinia": "catalog:", + "pinia-plugin-persistedstate": "catalog:", + "vue": "catalog:", + "vue-router": "catalog:" + } +} diff --git a/packages/stores/shim-pinia.d.ts b/packages/stores/shim-pinia.d.ts new file mode 100644 index 0000000..69be75d --- /dev/null +++ b/packages/stores/shim-pinia.d.ts @@ -0,0 +1,9 @@ +// https://github.com/vuejs/pinia/issues/2098 +declare module 'pinia' { + export function acceptHMRUpdate( + initialUseStore: any | StoreDefinition, + hot: any, + ): (newModule: any) => any; +} + +export {}; diff --git a/packages/stores/src/index.ts b/packages/stores/src/index.ts new file mode 100644 index 0000000..41b3662 --- /dev/null +++ b/packages/stores/src/index.ts @@ -0,0 +1,3 @@ +export * from './modules'; +export * from './setup'; +export { defineStore, storeToRefs } from 'pinia'; diff --git a/packages/stores/src/modules/access.test.ts b/packages/stores/src/modules/access.test.ts new file mode 100644 index 0000000..211e256 --- /dev/null +++ b/packages/stores/src/modules/access.test.ts @@ -0,0 +1,46 @@ +import { createPinia, setActivePinia } from 'pinia'; +import { beforeEach, describe, expect, it } from 'vitest'; + +import { useAccessStore } from './access'; + +describe('useAccessStore', () => { + beforeEach(() => { + setActivePinia(createPinia()); + }); + + it('updates accessMenus state', () => { + const store = useAccessStore(); + expect(store.accessMenus).toEqual([]); + store.setAccessMenus([{ name: 'Dashboard', path: '/dashboard' }]); + expect(store.accessMenus).toEqual([ + { name: 'Dashboard', path: '/dashboard' }, + ]); + }); + + it('updates accessToken state correctly', () => { + const store = useAccessStore(); + expect(store.accessToken).toBeNull(); // 初始状态 + store.setAccessToken('abc123'); + expect(store.accessToken).toBe('abc123'); + }); + + it('returns the correct accessToken', () => { + const store = useAccessStore(); + store.setAccessToken('xyz789'); + expect(store.accessToken).toBe('xyz789'); + }); + + // 测试设置空的访问菜单列表 + it('handles empty accessMenus correctly', () => { + const store = useAccessStore(); + store.setAccessMenus([]); + expect(store.accessMenus).toEqual([]); + }); + + // 测试设置空的访问路由列表 + it('handles empty accessRoutes correctly', () => { + const store = useAccessStore(); + store.setAccessRoutes([]); + expect(store.accessRoutes).toEqual([]); + }); +}); diff --git a/packages/stores/src/modules/access.ts b/packages/stores/src/modules/access.ts new file mode 100644 index 0000000..6b44520 --- /dev/null +++ b/packages/stores/src/modules/access.ts @@ -0,0 +1,104 @@ +import type { MenuRecordRaw } from '@vben-core/typings'; +import type { RouteRecordRaw } from 'vue-router'; + +import { acceptHMRUpdate, defineStore } from 'pinia'; + +type AccessToken = null | string; + +interface AccessState { + /** + * 权限码 + */ + accessCodes: string[]; + /** + * 可访问的菜单列表 + */ + accessMenus: MenuRecordRaw[]; + /** + * 可访问的路由列表 + */ + accessRoutes: RouteRecordRaw[]; + /** + * 登录 accessToken + */ + accessToken: AccessToken; + /** + * 是否已经检查过权限 + */ + isAccessChecked: boolean; + /** + * 登录是否过期 + */ + loginExpired: boolean; + /** + * 登录 accessToken + */ + refreshToken: AccessToken; +} + +/** + * @zh_CN 访问权限相关 + */ +export const useAccessStore = defineStore('core-access', { + actions: { + getMenuByPath(path: string) { + function findMenu( + menus: MenuRecordRaw[], + path: string, + ): MenuRecordRaw | undefined { + for (const menu of menus) { + if (menu.path === path) { + return menu; + } + if (menu.children) { + const matched = findMenu(menu.children, path); + if (matched) { + return matched; + } + } + } + } + return findMenu(this.accessMenus, path); + }, + setAccessCodes(codes: string[]) { + this.accessCodes = codes; + }, + setAccessMenus(menus: MenuRecordRaw[]) { + this.accessMenus = menus; + }, + setAccessRoutes(routes: RouteRecordRaw[]) { + this.accessRoutes = routes; + }, + setAccessToken(token: AccessToken) { + this.accessToken = token; + }, + setIsAccessChecked(isAccessChecked: boolean) { + this.isAccessChecked = isAccessChecked; + }, + setLoginExpired(loginExpired: boolean) { + this.loginExpired = loginExpired; + }, + setRefreshToken(token: AccessToken) { + this.refreshToken = token; + }, + }, + persist: { + // 持久化 + pick: ['accessToken', 'refreshToken', 'accessCodes'], + }, + state: (): AccessState => ({ + accessCodes: [], + accessMenus: [], + accessRoutes: [], + accessToken: null, + isAccessChecked: false, + loginExpired: false, + refreshToken: null, + }), +}); + +// 解决热更新问题 +const hot = import.meta.hot; +if (hot) { + hot.accept(acceptHMRUpdate(useAccessStore, hot)); +} diff --git a/packages/stores/src/modules/index.ts b/packages/stores/src/modules/index.ts new file mode 100644 index 0000000..7941cbe --- /dev/null +++ b/packages/stores/src/modules/index.ts @@ -0,0 +1,4 @@ +export * from './access'; +export * from './lock'; +export * from './tabbar'; +export * from './user'; diff --git a/packages/stores/src/modules/lock.test.ts b/packages/stores/src/modules/lock.test.ts new file mode 100644 index 0000000..d8dbe55 --- /dev/null +++ b/packages/stores/src/modules/lock.test.ts @@ -0,0 +1,31 @@ +import { createPinia, setActivePinia } from 'pinia'; +import { beforeEach, describe, expect, it } from 'vitest'; + +import { useLockStore } from './lock'; + +describe('useLockStore', () => { + beforeEach(() => { + setActivePinia(createPinia()); + }); + + it('should initialize with correct default state', () => { + const store = useLockStore(); + expect(store.isLockScreen).toBe(false); + expect(store.lockScreenPassword).toBeUndefined(); + }); + + it('should lock screen with a password', () => { + const store = useLockStore(); + store.lockScreen('1234'); + expect(store.isLockScreen).toBe(true); + expect(store.lockScreenPassword).toBe('1234'); + }); + + it('should unlock screen and clear password', () => { + const store = useLockStore(); + store.lockScreen('1234'); + store.unlockScreen(); + expect(store.isLockScreen).toBe(false); + expect(store.lockScreenPassword).toBeUndefined(); + }); +}); diff --git a/packages/stores/src/modules/lock.ts b/packages/stores/src/modules/lock.ts new file mode 100644 index 0000000..76a9600 --- /dev/null +++ b/packages/stores/src/modules/lock.ts @@ -0,0 +1,33 @@ +import { defineStore } from 'pinia'; + +interface AppState { + /** + * 是否锁屏状态 + */ + isLockScreen: boolean; + /** + * 锁屏密码 + */ + lockScreenPassword?: string; +} + +export const useLockStore = defineStore('core-lock', { + actions: { + lockScreen(password: string) { + this.isLockScreen = true; + this.lockScreenPassword = password; + }, + + unlockScreen() { + this.isLockScreen = false; + this.lockScreenPassword = undefined; + }, + }, + persist: { + pick: ['isLockScreen', 'lockScreenPassword'], + }, + state: (): AppState => ({ + isLockScreen: false, + lockScreenPassword: undefined, + }), +}); diff --git a/packages/stores/src/modules/tabbar.test.ts b/packages/stores/src/modules/tabbar.test.ts new file mode 100644 index 0000000..d74615c --- /dev/null +++ b/packages/stores/src/modules/tabbar.test.ts @@ -0,0 +1,295 @@ +import { createRouter, createWebHistory } from 'vue-router'; + +import { createPinia, setActivePinia } from 'pinia'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { useTabbarStore } from './tabbar'; + +describe('useAccessStore', () => { + const router = createRouter({ + history: createWebHistory(), + routes: [], + }); + router.push = vi.fn(); + router.replace = vi.fn(); + beforeEach(() => { + setActivePinia(createPinia()); + vi.clearAllMocks(); + }); + + it('adds a new tab', () => { + const store = useTabbarStore(); + const tab: any = { + fullPath: '/home', + meta: {}, + name: 'Home', + path: '/home', + }; + store.addTab(tab); + expect(store.tabs.length).toBe(1); + expect(store.tabs[0]).toEqual(tab); + }); + + it('adds a new tab if it does not exist', () => { + const store = useTabbarStore(); + const newTab: any = { + fullPath: '/new', + meta: {}, + name: 'New', + path: '/new', + }; + store.addTab(newTab); + expect(store.tabs).toContainEqual(newTab); + }); + + it('updates an existing tab instead of adding a new one', () => { + const store = useTabbarStore(); + const initialTab: any = { + fullPath: '/existing', + meta: {}, + name: 'Existing', + path: '/existing', + query: {}, + }; + store.tabs.push(initialTab); + const updatedTab = { ...initialTab, query: { id: '1' } }; + store.addTab(updatedTab); + expect(store.tabs.length).toBe(1); + expect(store.tabs[0]?.query).toEqual({ id: '1' }); + }); + + it('closes all tabs', async () => { + const store = useTabbarStore(); + store.tabs = [ + { fullPath: '/home', meta: {}, name: 'Home', path: '/home' }, + ] as any; + router.replace = vi.fn(); + + await store.closeAllTabs(router); + + expect(store.tabs.length).toBe(1); + }); + + it('closes a non-affix tab', () => { + const store = useTabbarStore(); + const tab: any = { + fullPath: '/closable', + meta: {}, + name: 'Closable', + path: '/closable', + }; + store.tabs.push(tab); + store._close(tab); + expect(store.tabs.length).toBe(0); + }); + + it('does not close an affix tab', () => { + const store = useTabbarStore(); + const affixTab: any = { + fullPath: '/affix', + meta: { affixTab: true }, + name: 'Affix', + path: '/affix', + }; + store.tabs.push(affixTab); + store._close(affixTab); + expect(store.tabs.length).toBe(1); // Affix tab should not be closed + }); + + it('returns all cache tabs', () => { + const store = useTabbarStore(); + store.cachedTabs.add('Home'); + store.cachedTabs.add('About'); + expect(store.getCachedTabs).toEqual(['Home', 'About']); + }); + + it('returns all tabs, including affix tabs', () => { + const store = useTabbarStore(); + const normalTab: any = { + fullPath: '/normal', + meta: {}, + name: 'Normal', + path: '/normal', + }; + const affixTab: any = { + fullPath: '/affix', + meta: { affixTab: true }, + name: 'Affix', + path: '/affix', + }; + store.tabs.push(normalTab); + store.affixTabs.push(affixTab); + expect(store.getTabs).toContainEqual(normalTab); + expect(store.affixTabs).toContainEqual(affixTab); + }); + + it('navigates to a specific tab', async () => { + const store = useTabbarStore(); + const tab: any = { meta: {}, name: 'Dashboard', path: '/dashboard' }; + + await store._goToTab(tab, router); + + expect(router.replace).toHaveBeenCalledWith({ + params: {}, + path: '/dashboard', + query: {}, + }); + }); + + it('closes multiple tabs by paths', async () => { + const store = useTabbarStore(); + store.addTab({ + fullPath: '/home', + meta: {}, + name: 'Home', + path: '/home', + } as any); + store.addTab({ + fullPath: '/about', + meta: {}, + name: 'About', + path: '/about', + } as any); + store.addTab({ + fullPath: '/contact', + meta: {}, + name: 'Contact', + path: '/contact', + } as any); + + await store._bulkCloseByPaths(['/home', '/contact']); + + expect(store.tabs).toHaveLength(1); + expect(store.tabs[0]?.name).toBe('About'); + }); + + it('closes all tabs to the left of the specified tab', async () => { + const store = useTabbarStore(); + store.addTab({ + fullPath: '/home', + meta: {}, + name: 'Home', + path: '/home', + } as any); + store.addTab({ + fullPath: '/about', + meta: {}, + name: 'About', + path: '/about', + } as any); + const targetTab: any = { + fullPath: '/contact', + meta: {}, + name: 'Contact', + path: '/contact', + }; + store.addTab(targetTab); + + await store.closeLeftTabs(targetTab); + + expect(store.tabs).toHaveLength(1); + expect(store.tabs[0]?.name).toBe('Contact'); + }); + + it('closes all tabs except the specified tab', async () => { + const store = useTabbarStore(); + store.addTab({ + fullPath: '/home', + meta: {}, + name: 'Home', + path: '/home', + } as any); + const targetTab: any = { + fullPath: '/about', + meta: {}, + name: 'About', + path: '/about', + }; + store.addTab(targetTab); + store.addTab({ + fullPath: '/contact', + meta: {}, + name: 'Contact', + path: '/contact', + } as any); + + await store.closeOtherTabs(targetTab); + + expect(store.tabs).toHaveLength(1); + expect(store.tabs[0]?.name).toBe('About'); + }); + + it('closes all tabs to the right of the specified tab', async () => { + const store = useTabbarStore(); + const targetTab: any = { + fullPath: '/home', + meta: {}, + name: 'Home', + path: '/home', + }; + store.addTab(targetTab); + store.addTab({ + fullPath: '/about', + meta: {}, + name: 'About', + path: '/about', + } as any); + store.addTab({ + fullPath: '/contact', + meta: {}, + name: 'Contact', + path: '/contact', + } as any); + + await store.closeRightTabs(targetTab); + + expect(store.tabs).toHaveLength(1); + expect(store.tabs[0]?.name).toBe('Home'); + }); + + it('closes the tab with the specified key', async () => { + const store = useTabbarStore(); + const keyToClose = '/about'; + store.addTab({ + fullPath: '/home', + meta: {}, + name: 'Home', + path: '/home', + } as any); + store.addTab({ + fullPath: keyToClose, + meta: {}, + name: 'About', + path: '/about', + } as any); + store.addTab({ + fullPath: '/contact', + meta: {}, + name: 'Contact', + path: '/contact', + } as any); + + await store.closeTabByKey(keyToClose, router); + + expect(store.tabs).toHaveLength(2); + expect( + store.tabs.find((tab) => tab.fullPath === keyToClose), + ).toBeUndefined(); + }); + + it('refreshes the current tab', async () => { + const store = useTabbarStore(); + const currentTab: any = { + fullPath: '/dashboard', + meta: { name: 'Dashboard' }, + name: 'Dashboard', + path: '/dashboard', + }; + router.currentRoute.value = currentTab; + + await store.refresh(router); + + expect(store.excludeCachedTabs.has('Dashboard')).toBe(false); + expect(store.renderRouteView).toBe(true); + }); +}); diff --git a/packages/stores/src/modules/tabbar.ts b/packages/stores/src/modules/tabbar.ts new file mode 100644 index 0000000..0555437 --- /dev/null +++ b/packages/stores/src/modules/tabbar.ts @@ -0,0 +1,567 @@ +import type { Router, RouteRecordNormalized } from 'vue-router'; + +import type { TabDefinition } from '@vben-core/typings'; + +import { toRaw } from 'vue'; + +import { preferences } from '@vben-core/preferences'; +import { + openRouteInNewWindow, + startProgress, + stopProgress, +} from '@vben-core/shared/utils'; + +import { acceptHMRUpdate, defineStore } from 'pinia'; + +interface TabbarState { + /** + * @zh_CN 当前打开的标签页列表缓存 + */ + cachedTabs: Set; + /** + * @zh_CN 拖拽结束的索引 + */ + dragEndIndex: number; + /** + * @zh_CN 需要排除缓存的标签页 + */ + excludeCachedTabs: Set; + /** + * @zh_CN 是否刷新 + */ + renderRouteView?: boolean; + /** + * @zh_CN 当前打开的标签页列表 + */ + tabs: TabDefinition[]; + /** + * @zh_CN 更新时间,用于一些更新场景,使用watch深度监听的话,会损耗性能 + */ + updateTime?: number; +} + +/** + * @zh_CN 访问权限相关 + */ +export const useTabbarStore = defineStore('core-tabbar', { + actions: { + /** + * Close tabs in bulk + */ + async _bulkCloseByPaths(paths: string[]) { + this.tabs = this.tabs.filter((item) => { + return !paths.includes(getTabPath(item)); + }); + + this.updateCacheTabs(); + }, + /** + * @zh_CN 关闭标签页 + * @param tab + */ + _close(tab: TabDefinition) { + const { fullPath } = tab; + if (isAffixTab(tab)) { + return; + } + const index = this.tabs.findIndex((item) => item.fullPath === fullPath); + index !== -1 && this.tabs.splice(index, 1); + }, + /** + * @zh_CN 跳转到默认标签页 + */ + async _goToDefaultTab(router: Router) { + if (this.getTabs.length <= 0) { + return; + } + const firstTab = this.getTabs[0]; + if (firstTab) { + await this._goToTab(firstTab, router); + } + }, + /** + * @zh_CN 跳转到标签页 + * @param tab + * @param router + */ + async _goToTab(tab: TabDefinition, router: Router) { + const { params, path, query } = tab; + const toParams = { + params: params || {}, + path, + query: query || {}, + }; + await router.replace(toParams); + }, + /** + * @zh_CN 添加标签页 + * @param routeTab + */ + addTab(routeTab: TabDefinition) { + const tab = cloneTab(routeTab); + if (!isTabShown(tab)) { + return; + } + + const tabIndex = this.tabs.findIndex((tab) => { + return getTabPath(tab) === getTabPath(routeTab); + }); + + if (tabIndex === -1) { + const maxCount = preferences.tabbar.maxCount; + // 获取动态路由打开数,超过 0 即代表需要控制打开数 + const maxNumOfOpenTab = (routeTab?.meta?.maxNumOfOpenTab ?? + -1) as number; + // 如果动态路由层级大于 0 了,那么就要限制该路由的打开数限制了 + // 获取到已经打开的动态路由数, 判断是否大于某一个值 + if ( + maxNumOfOpenTab > 0 && + this.tabs.filter((tab) => tab.name === routeTab.name).length >= + maxNumOfOpenTab + ) { + // 关闭第一个 + const index = this.tabs.findIndex( + (item) => item.name === routeTab.name, + ); + index !== -1 && this.tabs.splice(index, 1); + } else if (maxCount > 0 && this.tabs.length >= maxCount) { + // 关闭第一个 + const index = this.tabs.findIndex( + (item) => + !Reflect.has(item.meta, 'affixTab') || !item.meta.affixTab, + ); + index !== -1 && this.tabs.splice(index, 1); + } + this.tabs.push(tab); + } else { + // 页面已经存在,不重复添加选项卡,只更新选项卡参数 + const currentTab = toRaw(this.tabs)[tabIndex]; + const mergedTab = { + ...currentTab, + ...tab, + meta: { ...currentTab?.meta, ...tab.meta }, + }; + if (currentTab) { + const curMeta = currentTab.meta; + if (Reflect.has(curMeta, 'affixTab')) { + mergedTab.meta.affixTab = curMeta.affixTab; + } + if (Reflect.has(curMeta, 'newTabTitle')) { + mergedTab.meta.newTabTitle = curMeta.newTabTitle; + } + } + + this.tabs.splice(tabIndex, 1, mergedTab); + } + this.updateCacheTabs(); + }, + /** + * @zh_CN 关闭所有标签页 + */ + async closeAllTabs(router: Router) { + const newTabs = this.tabs.filter((tab) => isAffixTab(tab)); + this.tabs = newTabs.length > 0 ? newTabs : [...this.tabs].splice(0, 1); + await this._goToDefaultTab(router); + this.updateCacheTabs(); + }, + /** + * @zh_CN 关闭左侧标签页 + * @param tab + */ + async closeLeftTabs(tab: TabDefinition) { + const index = this.tabs.findIndex( + (item) => getTabPath(item) === getTabPath(tab), + ); + + if (index < 1) { + return; + } + + const leftTabs = this.tabs.slice(0, index); + const paths: string[] = []; + + for (const item of leftTabs) { + if (!isAffixTab(item)) { + paths.push(getTabPath(item)); + } + } + await this._bulkCloseByPaths(paths); + }, + /** + * @zh_CN 关闭其他标签页 + * @param tab + */ + async closeOtherTabs(tab: TabDefinition) { + const closePaths = this.tabs.map((item) => getTabPath(item)); + + const paths: string[] = []; + + for (const path of closePaths) { + if (path !== tab.fullPath) { + const closeTab = this.tabs.find((item) => getTabPath(item) === path); + if (!closeTab) { + continue; + } + if (!isAffixTab(closeTab)) { + paths.push(getTabPath(closeTab)); + } + } + } + await this._bulkCloseByPaths(paths); + }, + /** + * @zh_CN 关闭右侧标签页 + * @param tab + */ + async closeRightTabs(tab: TabDefinition) { + const index = this.tabs.findIndex( + (item) => getTabPath(item) === getTabPath(tab), + ); + + if (index !== -1 && index < this.tabs.length - 1) { + const rightTabs = this.tabs.slice(index + 1); + + const paths: string[] = []; + for (const item of rightTabs) { + if (!isAffixTab(item)) { + paths.push(getTabPath(item)); + } + } + await this._bulkCloseByPaths(paths); + } + }, + + /** + * @zh_CN 关闭标签页 + * @param tab + * @param router + */ + async closeTab(tab: TabDefinition, router: Router) { + const { currentRoute } = router; + + // 关闭不是激活选项卡 + if (getTabPath(currentRoute.value) !== getTabPath(tab)) { + this._close(tab); + this.updateCacheTabs(); + return; + } + const index = this.getTabs.findIndex( + (item) => getTabPath(item) === getTabPath(currentRoute.value), + ); + + const before = this.getTabs[index - 1]; + const after = this.getTabs[index + 1]; + + // 下一个tab存在,跳转到下一个 + if (after) { + this._close(tab); + await this._goToTab(after, router); + // 上一个tab存在,跳转到上一个 + } else if (before) { + this._close(tab); + await this._goToTab(before, router); + } else { + console.error('Failed to close the tab; only one tab remains open.'); + } + }, + + /** + * @zh_CN 通过key关闭标签页 + * @param key + * @param router + */ + async closeTabByKey(key: string, router: Router) { + const originKey = decodeURIComponent(key); + const index = this.tabs.findIndex( + (item) => getTabPath(item) === originKey, + ); + if (index === -1) { + return; + } + + const tab = this.tabs[index]; + if (tab) { + await this.closeTab(tab, router); + } + }, + + /** + * 根据路径获取标签页 + * @param path + */ + getTabByPath(path: string) { + return this.getTabs.find( + (item) => getTabPath(item) === path, + ) as TabDefinition; + }, + /** + * @zh_CN 新窗口打开标签页 + * @param tab + */ + async openTabInNewWindow(tab: TabDefinition) { + openRouteInNewWindow(tab.fullPath || tab.path); + }, + + /** + * @zh_CN 固定标签页 + * @param tab + */ + async pinTab(tab: TabDefinition) { + const index = this.tabs.findIndex( + (item) => getTabPath(item) === getTabPath(tab), + ); + if (index !== -1) { + const oldTab = this.tabs[index]; + tab.meta.affixTab = true; + tab.meta.title = oldTab?.meta?.title as string; + // this.addTab(tab); + this.tabs.splice(index, 1, tab); + } + // 过滤固定tabs,后面更改affixTabOrder的值的话可能会有问题,目前行464排序affixTabs没有设置值 + const affixTabs = this.tabs.filter((tab) => isAffixTab(tab)); + // 获得固定tabs的index + const newIndex = affixTabs.findIndex( + (item) => getTabPath(item) === getTabPath(tab), + ); + // 交换位置重新排序 + await this.sortTabs(index, newIndex); + }, + + /** + * 刷新标签页 + */ + async refresh(router: Router) { + const { currentRoute } = router; + const { name } = currentRoute.value; + + this.excludeCachedTabs.add(name as string); + this.renderRouteView = false; + startProgress(); + + await new Promise((resolve) => setTimeout(resolve, 200)); + + this.excludeCachedTabs.delete(name as string); + this.renderRouteView = true; + stopProgress(); + }, + + /** + * @zh_CN 重置标签页标题 + */ + async resetTabTitle(tab: TabDefinition) { + if (tab?.meta?.newTabTitle) { + return; + } + const findTab = this.tabs.find( + (item) => getTabPath(item) === getTabPath(tab), + ); + if (findTab) { + findTab.meta.newTabTitle = undefined; + await this.updateCacheTabs(); + } + }, + + /** + * 设置固定标签页 + * @param tabs + */ + setAffixTabs(tabs: RouteRecordNormalized[]) { + for (const tab of tabs) { + tab.meta.affixTab = true; + this.addTab(routeToTab(tab)); + } + }, + + /** + * @zh_CN 设置标签页标题 + * @param tab + * @param title + */ + async setTabTitle(tab: TabDefinition, title: string) { + const findTab = this.tabs.find( + (item) => getTabPath(item) === getTabPath(tab), + ); + + if (findTab) { + findTab.meta.newTabTitle = title; + + await this.updateCacheTabs(); + } + }, + + setUpdateTime() { + this.updateTime = Date.now(); + }, + /** + * @zh_CN 设置标签页顺序 + * @param oldIndex + * @param newIndex + */ + async sortTabs(oldIndex: number, newIndex: number) { + const currentTab = this.tabs[oldIndex]; + if (!currentTab) { + return; + } + this.tabs.splice(oldIndex, 1); + this.tabs.splice(newIndex, 0, currentTab); + this.dragEndIndex = this.dragEndIndex + 1; + }, + /** + * @zh_CN 切换固定标签页 + * @param tab + */ + async toggleTabPin(tab: TabDefinition) { + const affixTab = tab?.meta?.affixTab ?? false; + + await (affixTab ? this.unpinTab(tab) : this.pinTab(tab)); + }, + + /** + * @zh_CN 取消固定标签页 + * @param tab + */ + async unpinTab(tab: TabDefinition) { + const index = this.tabs.findIndex( + (item) => getTabPath(item) === getTabPath(tab), + ); + + if (index !== -1) { + const oldTab = this.tabs[index]; + tab.meta.affixTab = false; + tab.meta.title = oldTab?.meta?.title as string; + // this.addTab(tab); + this.tabs.splice(index, 1, tab); + } + // 过滤固定tabs,后面更改affixTabOrder的值的话可能会有问题,目前行464排序affixTabs没有设置值 + const affixTabs = this.tabs.filter((tab) => isAffixTab(tab)); + // 获得固定tabs的index,使用固定tabs的下一个位置也就是活动tabs的第一个位置 + const newIndex = affixTabs.length; + // 交换位置重新排序 + await this.sortTabs(index, newIndex); + }, + + /** + * 根据当前打开的选项卡更新缓存 + */ + async updateCacheTabs() { + const cacheMap = new Set(); + + for (const tab of this.tabs) { + // 跳过不需要持久化的标签页 + const keepAlive = tab.meta?.keepAlive; + if (!keepAlive) { + continue; + } + (tab.matched || []).forEach((t, i) => { + if (i > 0) { + cacheMap.add(t.name as string); + } + }); + + const name = tab.name as string; + cacheMap.add(name); + } + this.cachedTabs = cacheMap; + }, + }, + getters: { + affixTabs(): TabDefinition[] { + const affixTabs = this.tabs.filter((tab) => isAffixTab(tab)); + + return affixTabs.sort((a, b) => { + const orderA = (a.meta?.affixTabOrder ?? 0) as number; + const orderB = (b.meta?.affixTabOrder ?? 0) as number; + return orderA - orderB; + }); + }, + getCachedTabs(): string[] { + return [...this.cachedTabs]; + }, + getExcludeCachedTabs(): string[] { + return [...this.excludeCachedTabs]; + }, + getTabs(): TabDefinition[] { + const normalTabs = this.tabs.filter((tab) => !isAffixTab(tab)); + return [...this.affixTabs, ...normalTabs].filter(Boolean); + }, + }, + persist: [ + // tabs不需要保存在localStorage + { + pick: ['tabs'], + storage: sessionStorage, + }, + ], + state: (): TabbarState => ({ + cachedTabs: new Set(), + dragEndIndex: 0, + excludeCachedTabs: new Set(), + renderRouteView: true, + tabs: [], + updateTime: Date.now(), + }), +}); + +// 解决热更新问题 +const hot = import.meta.hot; +if (hot) { + hot.accept(acceptHMRUpdate(useTabbarStore, hot)); +} + +/** + * @zh_CN 克隆路由,防止路由被修改 + * @param route + */ +function cloneTab(route: TabDefinition): TabDefinition { + if (!route) { + return route; + } + const { matched, meta, ...opt } = route; + return { + ...opt, + matched: (matched + ? matched.map((item) => ({ + meta: item.meta, + name: item.name, + path: item.path, + })) + : undefined) as RouteRecordNormalized[], + meta: { + ...meta, + newTabTitle: meta.newTabTitle, + }, + }; +} + +/** + * @zh_CN 是否是固定标签页 + * @param tab + */ +function isAffixTab(tab: TabDefinition) { + return tab?.meta?.affixTab ?? false; +} + +/** + * @zh_CN 是否显示标签 + * @param tab + */ +function isTabShown(tab: TabDefinition) { + const matched = tab?.matched ?? []; + return !tab.meta.hideInTab && matched.every((item) => !item.meta.hideInTab); +} + +/** + * @zh_CN 获取标签页路径 + * @param tab + */ +function getTabPath(tab: RouteRecordNormalized | TabDefinition) { + return decodeURIComponent((tab as TabDefinition).fullPath || tab.path); +} + +function routeToTab(route: RouteRecordNormalized) { + return { + meta: route.meta, + name: route.name, + path: route.path, + } as TabDefinition; +} diff --git a/packages/stores/src/modules/user.test.ts b/packages/stores/src/modules/user.test.ts new file mode 100644 index 0000000..3d8a22c --- /dev/null +++ b/packages/stores/src/modules/user.test.ts @@ -0,0 +1,37 @@ +import { createPinia, setActivePinia } from 'pinia'; +import { beforeEach, describe, expect, it } from 'vitest'; + +import { useUserStore } from './user'; + +describe('useUserStore', () => { + beforeEach(() => { + setActivePinia(createPinia()); + }); + + it('returns correct userInfo', () => { + const store = useUserStore(); + const userInfo: any = { name: 'Jane Doe', roles: [{ value: 'user' }] }; + store.setUserInfo(userInfo); + expect(store.userInfo).toEqual(userInfo); + }); + + // 测试重置用户信息时的行为 + it('clears userInfo and userRoles when setting null userInfo', () => { + const store = useUserStore(); + store.setUserInfo({ + roles: [{ roleName: 'User', value: 'user' }], + } as any); + expect(store.userInfo).not.toBeNull(); + expect(store.userRoles.length).toBeGreaterThan(0); + + store.setUserInfo(null as any); + expect(store.userInfo).toBeNull(); + expect(store.userRoles).toEqual([]); + }); + + // 测试在没有用户角色时返回空数组 + it('returns an empty array for userRoles if not set', () => { + const store = useUserStore(); + expect(store.userRoles).toEqual([]); + }); +}); diff --git a/packages/stores/src/modules/user.ts b/packages/stores/src/modules/user.ts new file mode 100644 index 0000000..afc974a --- /dev/null +++ b/packages/stores/src/modules/user.ts @@ -0,0 +1,68 @@ +import { acceptHMRUpdate, defineStore } from 'pinia'; + +interface BasicUserInfo { + [key: string]: any; + /** + * 头像 + */ + avatar: string; + /** + * 用户权限 + */ + permissions: string[]; + /** + * 用户昵称 + */ + realName: string; + /** + * 用户角色 + */ + roles: string[]; + /** + * 用户id + */ + userId: number | string; + /** + * 用户名 + */ + username: string; +} + +interface AccessState { + /** + * 用户信息 + */ + userInfo: BasicUserInfo | null; + /** + * 用户角色 + */ + userRoles: string[]; +} + +/** + * @zh_CN 用户信息相关 + */ +export const useUserStore = defineStore('core-user', { + actions: { + setUserInfo(userInfo: BasicUserInfo | null) { + // 设置用户信息 + this.userInfo = userInfo; + // 设置角色信息 + const roles = userInfo?.roles ?? []; + this.setUserRoles(roles); + }, + setUserRoles(roles: string[]) { + this.userRoles = roles; + }, + }, + state: (): AccessState => ({ + userInfo: null, + userRoles: [], + }), +}); + +// 解决热更新问题 +const hot = import.meta.hot; +if (hot) { + hot.accept(acceptHMRUpdate(useUserStore, hot)); +} diff --git a/packages/stores/src/setup.ts b/packages/stores/src/setup.ts new file mode 100644 index 0000000..890c4dc --- /dev/null +++ b/packages/stores/src/setup.ts @@ -0,0 +1,43 @@ +import type { Pinia } from 'pinia'; + +import type { App } from 'vue'; + +import { createPinia } from 'pinia'; + +let pinia: Pinia; + +export interface InitStoreOptions { + /** + * @zh_CN 应用名,由于 @vben/stores 是公用的,后续可能有多个app,为了防止多个app缓存冲突,可在这里配置应用名,应用名将被用于持久化的前缀 + */ + namespace: string; +} + +/** + * @zh_CN 初始化pinia + */ +export async function initStores(app: App, options: InitStoreOptions) { + const { createPersistedState } = await import('pinia-plugin-persistedstate'); + pinia = createPinia(); + const { namespace } = options; + pinia.use( + createPersistedState({ + // key $appName-$store.id + key: (storeKey) => `${namespace}-${storeKey}`, + storage: localStorage, + }), + ); + app.use(pinia); + return pinia; +} + +export function resetAllStores() { + if (!pinia) { + console.error('Pinia is not installed'); + return; + } + const allStores = (pinia as any)._s; + for (const [_key, store] of allStores) { + store.$reset(); + } +} diff --git a/packages/stores/tsconfig.json b/packages/stores/tsconfig.json new file mode 100644 index 0000000..3057820 --- /dev/null +++ b/packages/stores/tsconfig.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src", "shim-pinia.d.ts"] +} diff --git a/packages/styles/README.md b/packages/styles/README.md new file mode 100644 index 0000000..4826446 --- /dev/null +++ b/packages/styles/README.md @@ -0,0 +1,19 @@ +# @vben/styles + +用于多个 `app` 公用的样式文件,继承了 `@vben-core/design` 的所有能力。业务上有通用的样式文件可以放在这里。 + +## 用法 + +### 添加依赖 + +```bash +# 进入目标应用目录,例如 apps/xxxx-app +# cd apps/xxxx-app +pnpm add @vben/styles +``` + +### 使用 + +```ts +import '@vben/styles'; +``` diff --git a/packages/styles/package.json b/packages/styles/package.json new file mode 100644 index 0000000..b8d96da --- /dev/null +++ b/packages/styles/package.json @@ -0,0 +1,34 @@ +{ + "name": "@vben/styles", + "version": "5.5.3", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/styles" + }, + "license": "MIT", + "type": "module", + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + }, + "./antd": { + "default": "./src/antd/index.css" + }, + "./ele": { + "default": "./src/ele/index.css" + }, + "./naive": { + "default": "./src/naive/index.css" + }, + "./global": { + "default": "./src/global/index.scss" + } + }, + "dependencies": { + "@vben-core/design": "workspace:*" + } +} diff --git a/packages/styles/src/antd/index.css b/packages/styles/src/antd/index.css new file mode 100644 index 0000000..3e47c89 --- /dev/null +++ b/packages/styles/src/antd/index.css @@ -0,0 +1,133 @@ +/* ant-design-vue 组件库的一些样式重置 */ + +.ant-app { + width: 100%; + height: 100%; + overscroll-behavior: none; + color: inherit; +} + +.ant-btn { + .anticon { + display: inline-flex; + } + + /* * 修复按钮添加图标时的位置问题 */ + > svg { + display: inline-block; + } + + > svg + span { + margin-inline-start: 6px; + } +} + +.ant-tag { + > svg { + display: inline-block; + } + + > svg + span { + margin-inline-start: 4px; + } +} + +.ant-message-notice-content, +.ant-notification-notice { + @apply dark:border-border/60 dark:border; +} + +.form-valid-error { + /** select 选择器的样式 */ + + .ant-select:not(.valid-success) .ant-select-selector:not(.valid-success) { + border-color: hsl(var(--destructive)) !important; + } + + .ant-select-focused .ant-select-selector { + box-shadow: 0 0 0 2px rgb(255 38 5 / 6%) !important; + } + + /** 数字输入框样式 */ + .ant-input-number-focused { + box-shadow: 0 0 0 2px rgb(255 38 5 / 6%); + } + + /** 密码输入框样式 */ + .ant-input-affix-wrapper:hover { + border-color: hsl(var(--destructive)); + box-shadow: 0 0 0 2px rgb(255 38 5 / 6%); + } + + .ant-input:not(.valid-success) { + border-color: hsl(var(--destructive)) !important; + } +} + +/** 区间选择器下面来回切换时的样式 */ +.ant-app .form-valid-error .ant-picker-active-bar { + background-color: hsl(var(--destructive)); +} + +/** 时间选择器的样式 */ +.ant-app .form-valid-error .ant-picker-focused { + box-shadow: 0 0 0 2px rgb(255 38 5 / 6%); +} + +/** 前后置小圆点样式 */ +.dot-before-common { + @apply before:relative before:top-[-2px] before:mr-[5px] before:inline-block before:size-[6px] before:rounded-full before:content-['']; +} + +.dot-before-red { + @apply dot-before-common before:bg-red-500; +} + +.dot-before-green { + @apply dot-before-common before:bg-green-500; +} + +/** +vxe表格右上角toolbar元素之间的间距 +*/ +.vxe-button + .vxe-button.type--button { + margin-left: 8px !important; +} + +/** +vxe默认圆角 +*/ +html { + --vxe-ui-border-radius: 8px !important; +} + +/** +vxe表格loading 只加载表格 不加载上面的表单 +*/ +.vxe-grid.is--loading::before { + content: none !important; +} + +/** +自定义success按钮样式 +ghost按钮专用! +*/ +.btn-success { + color: hsl(var(--success)) !important; + border-color: hsl(var(--success)) !important; +} + +.btn-success:hover { + color: hsl(var(--success) / 50%) !important; + border-color: hsl(var(--success) / 50%) !important; +} + +html.dark button[disabled].btn-success { + color: rgb(242 242 242 / 25%) !important; + border-color: hsl(240deg 3.7% 22%) !important; +} + +button[disabled].btn-success { + color: rgb(50 54 57 / 25%) !important; + border-color: hsl(240deg 5.9% 90%) !important; +} diff --git a/packages/styles/src/ele/index.css b/packages/styles/src/ele/index.css new file mode 100644 index 0000000..4ee8f7b --- /dev/null +++ b/packages/styles/src/ele/index.css @@ -0,0 +1,44 @@ +.el-card { + --el-card-border-radius: var(--radius) !important; +} + +.form-valid-error { + /** select 选择器的样式 */ + .el-select .el-select__wrapper { + box-shadow: 0 0 0 1px var(--el-color-danger) inset; + } + + /** input 选择器的样式 */ + .el-input .el-input__wrapper { + box-shadow: 0 0 0 1px var(--el-color-danger) inset; + } + + /** radio和checkbox 选择器的样式 */ + .el-radio .el-radio__inner, + .el-checkbox .el-checkbox__inner { + border: 1px solid var(--el-color-danger); + } + + .el-checkbox-button .el-checkbox-button__inner, + .el-radio-button .el-radio-button__inner { + border: 1px solid var(--el-color-danger); + } + + .el-checkbox-button:first-child .el-checkbox-button__inner, + .el-radio-button:first-child .el-radio-button__inner { + border-left: 1px solid var(--el-color-danger); + } + + .el-checkbox-button:not(:first-child) .el-checkbox-button__inner, + .el-radio-button:not(:first-child) .el-radio-button__inner { + border-left: none; + } + + .el-textarea .el-textarea__inner { + border: 1px solid var(--el-color-danger); + } +} + +html .el-loading-mask { + z-index: 1000; +} diff --git a/packages/styles/src/global/index.scss b/packages/styles/src/global/index.scss new file mode 100644 index 0000000..c7af9bb --- /dev/null +++ b/packages/styles/src/global/index.scss @@ -0,0 +1 @@ +@use '@vben-core/design/bem' as *; diff --git a/packages/styles/src/index.ts b/packages/styles/src/index.ts new file mode 100644 index 0000000..b375456 --- /dev/null +++ b/packages/styles/src/index.ts @@ -0,0 +1 @@ +import '@vben-core/design'; diff --git a/packages/styles/src/naive/index.css b/packages/styles/src/naive/index.css new file mode 100644 index 0000000..a775116 --- /dev/null +++ b/packages/styles/src/naive/index.css @@ -0,0 +1,20 @@ +.form-valid-error { + .n-base-selection__state-border, + .n-input__state-border, + .n-radio-group__splitor { + border: var(--n-border-error); + } + + .n-radio-group .n-radio-button, + .n-radio-group .n-radio-group__splitor { + --n-button-border-color: rgb(255 56 96); + } + + .n-radio__dot { + --n-box-shadow: inset 0 0 0 1px rgb(255 56 96); + } + + .n-checkbox-box__border { + --n-border: 1px solid rgb(255 56 96); + } +} diff --git a/packages/styles/tsconfig.json b/packages/styles/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/packages/styles/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/types/README.md b/packages/types/README.md new file mode 100644 index 0000000..f796f31 --- /dev/null +++ b/packages/types/README.md @@ -0,0 +1,20 @@ +# @vben/types + +用于多个 `app` 公用的工具类型,继承了 `@vben-core/typings` 的所有能力。业务上有通用的类型定义可以放在这里。 + +## 用法 + +### 添加依赖 + +```bash +# 进入目标应用目录,例如 apps/xxxx-app +# cd apps/xxxx-app +pnpm add @vben/types +``` + +### 使用 + +```ts +// 推荐加上 type +import type { SelectOption } from '@vben/types'; +``` diff --git a/packages/types/global.d.ts b/packages/types/global.d.ts new file mode 100644 index 0000000..5cf2d38 --- /dev/null +++ b/packages/types/global.d.ts @@ -0,0 +1,44 @@ +import type { RouteMeta as IRouteMeta } from '@vben-core/typings'; + +import 'vue-router'; + +declare module 'vue-router' { + // eslint-disable-next-line @typescript-eslint/no-empty-object-type + interface RouteMeta extends IRouteMeta {} +} + +export interface VbenAdminProAppConfigRaw { + // 后端接口地址 + VITE_GLOB_API_URL: string; + // 客户端ID + VITE_GLOB_APP_CLIENT_ID: string; + // # 全局加密开关(即开启了加解密功能才会生效 不是全部接口加密 需要和后端对应) + VITE_GLOB_ENABLE_ENCRYPT: string; + // RSA请求解密私钥 + VITE_GLOB_RSA_PRIVATE_KEY: string; + // RSA请求加密公钥 + VITE_GLOB_RSA_PUBLIC_KEY: string; + // 是否开启sse 注意从配置文件获取的类型为string + VITE_GLOB_SSE_ENABLE: string; +} + +export interface ApplicationConfig { + // 后端接口地址 + apiURL: string; + // 客户端key + clientId: string; + // 全局加密开关(即开启了加解密功能才会生效 不是全部接口加密 需要和后端对应) + enableEncrypt: boolean; + // RSA响应解密私钥 + rsaPrivateKey: string; + // RSA请求加密公钥 + rsaPublicKey: string; + // 是否开启sse + sseEnable: boolean; +} + +declare global { + interface Window { + _VBEN_ADMIN_PRO_APP_CONF_: VbenAdminProAppConfigRaw; + } +} diff --git a/packages/types/package.json b/packages/types/package.json new file mode 100644 index 0000000..3b2323f --- /dev/null +++ b/packages/types/package.json @@ -0,0 +1,27 @@ +{ + "name": "@vben/types", + "version": "5.5.3", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/types" + }, + "license": "MIT", + "type": "module", + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + }, + "./global": { + "types": "./global.d.ts" + } + }, + "dependencies": { + "@vben-core/typings": "workspace:*", + "vue": "catalog:", + "vue-router": "catalog:" + } +} diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts new file mode 100644 index 0000000..1e266c4 --- /dev/null +++ b/packages/types/src/index.ts @@ -0,0 +1,2 @@ +export type * from './user'; +export type * from '@vben-core/typings'; diff --git a/packages/types/src/user.ts b/packages/types/src/user.ts new file mode 100644 index 0000000..82d1b12 --- /dev/null +++ b/packages/types/src/user.ts @@ -0,0 +1,11 @@ +import type { BasicUserInfo } from '@vben-core/typings'; + +/** 用户信息 */ +interface UserInfo extends BasicUserInfo { + /** + * 拓展使用 + */ + [key: string]: any; +} + +export type { UserInfo }; diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json new file mode 100644 index 0000000..ce1a891 --- /dev/null +++ b/packages/types/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/utils/README.md b/packages/utils/README.md new file mode 100644 index 0000000..f06068a --- /dev/null +++ b/packages/utils/README.md @@ -0,0 +1,19 @@ +# @vben/utils + +用于多个 `app` 公用的工具包,继承了 `@vben-core/shared/utils` 的所有能力。业务上有通用的工具函数可以放在这里。 + +## 用法 + +### 添加依赖 + +```bash +# 进入目标应用目录,例如 apps/xxxx-app +# cd apps/xxxx-app +pnpm add @vben/utils +``` + +### 使用 + +```ts +import { isString } from '@vben/utils'; +``` diff --git a/packages/utils/package.json b/packages/utils/package.json new file mode 100644 index 0000000..40e89db --- /dev/null +++ b/packages/utils/package.json @@ -0,0 +1,28 @@ +{ + "name": "@vben/utils", + "version": "5.5.3", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/utils" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben-core/shared": "workspace:*", + "@vben-core/typings": "workspace:*", + "file-type": "^19.5.0", + "vue-router": "catalog:" + } +} diff --git a/packages/utils/src/helpers/__tests__/enum-options.test.ts b/packages/utils/src/helpers/__tests__/enum-options.test.ts new file mode 100644 index 0000000..14bfce8 --- /dev/null +++ b/packages/utils/src/helpers/__tests__/enum-options.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from 'vitest'; + +import { optionsToEnum } from '../enum-options'; + +describe('optionsToEnum Test', () => { + it('should return an enum object', () => { + const genderOptions = [ + { label: '男', value: 1, enumName: 'GENDER_MALE' }, + { label: '女', value: 2, enumName: 'GENDER_FEMALE' }, + ] as const; + + const enumTest = optionsToEnum(genderOptions); + const male = enumTest.GENDER_MALE; + const female = enumTest.GENDER_FEMALE; + + expect(male).toBe(1); + expect(female).toBe(2); + }); +}); diff --git a/packages/utils/src/helpers/__tests__/find-menu-by-path.test.ts b/packages/utils/src/helpers/__tests__/find-menu-by-path.test.ts new file mode 100644 index 0000000..fc0d76b --- /dev/null +++ b/packages/utils/src/helpers/__tests__/find-menu-by-path.test.ts @@ -0,0 +1,88 @@ +import { describe, expect, it } from 'vitest'; + +import { findMenuByPath, findRootMenuByPath } from '../find-menu-by-path'; + +// 示例菜单数据 +const menus: any[] = [ + { path: '/', children: [] }, + { path: '/about', children: [] }, + { + path: '/contact', + children: [ + { path: '/contact/email', children: [] }, + { path: '/contact/phone', children: [] }, + ], + }, + { + path: '/services', + children: [ + { path: '/services/design', children: [] }, + { + path: '/services/development', + children: [{ path: '/services/development/web', children: [] }], + }, + ], + }, +]; + +describe('menu Finder Tests', () => { + it('finds a top-level menu', () => { + const menu = findMenuByPath(menus, '/about'); + expect(menu).toBeDefined(); + expect(menu?.path).toBe('/about'); + }); + + it('finds a nested menu', () => { + const menu = findMenuByPath(menus, '/services/development/web'); + expect(menu).toBeDefined(); + expect(menu?.path).toBe('/services/development/web'); + }); + + it('returns null for a non-existent path', () => { + const menu = findMenuByPath(menus, '/non-existent'); + expect(menu).toBeNull(); + }); + + it('handles empty menus list', () => { + const menu = findMenuByPath([], '/about'); + expect(menu).toBeNull(); + }); + + it('handles menu items without children', () => { + const menu = findMenuByPath( + [{ path: '/only', children: undefined }] as any[], + '/only', + ); + expect(menu).toBeDefined(); + expect(menu?.path).toBe('/only'); + }); + + it('finds root menu by path', () => { + const { findMenu, rootMenu, rootMenuPath } = findRootMenuByPath( + menus, + '/services/development/web', + ); + + expect(findMenu).toBeDefined(); + expect(rootMenu).toBeUndefined(); + expect(rootMenuPath).toBeUndefined(); + expect(findMenu?.path).toBe('/services/development/web'); + }); + + it('returns null for undefined or empty path', () => { + const menuUndefinedPath = findMenuByPath(menus); + const menuEmptyPath = findMenuByPath(menus, ''); + expect(menuUndefinedPath).toBeNull(); + expect(menuEmptyPath).toBeNull(); + }); + + it('checks for root menu when path does not exist', () => { + const { findMenu, rootMenu, rootMenuPath } = findRootMenuByPath( + menus, + '/non-existent', + ); + expect(findMenu).toBeNull(); + expect(rootMenu).toBeUndefined(); + expect(rootMenuPath).toBeUndefined(); + }); +}); diff --git a/packages/utils/src/helpers/__tests__/generate-menus.test.ts b/packages/utils/src/helpers/__tests__/generate-menus.test.ts new file mode 100644 index 0000000..9d98b5a --- /dev/null +++ b/packages/utils/src/helpers/__tests__/generate-menus.test.ts @@ -0,0 +1,236 @@ +import type { Router, RouteRecordRaw } from 'vue-router'; + +import { createRouter, createWebHistory } from 'vue-router'; + +import { describe, expect, it, vi } from 'vitest'; + +import { generateMenus } from '../generate-menus'; + +// Nested route setup to test child inclusion and hideChildrenInMenu functionality + +describe('generateMenus', () => { + // 模拟路由数据 + const mockRoutes = [ + { + meta: { icon: 'home-icon', title: '首页' }, + name: 'home', + path: '/home', + }, + { + meta: { hideChildrenInMenu: true, icon: 'about-icon', title: '关于' }, + name: 'about', + path: '/about', + children: [ + { + path: 'team', + name: 'team', + meta: { icon: 'team-icon', title: '团队' }, + }, + ], + }, + ] as RouteRecordRaw[]; + + // 模拟 Vue 路由器实例 + const mockRouter = { + getRoutes: vi.fn(() => [ + { name: 'home', path: '/home' }, + { name: 'about', path: '/about' }, + { name: 'team', path: '/about/team' }, + ]), + }; + + it('the correct menu list should be generated according to the route', async () => { + const expectedMenus = [ + { + badge: undefined, + badgeType: undefined, + badgeVariants: undefined, + icon: 'home-icon', + name: '首页', + order: undefined, + parent: undefined, + parents: undefined, + path: '/home', + show: true, + children: [], + }, + { + badge: undefined, + badgeType: undefined, + badgeVariants: undefined, + icon: 'about-icon', + name: '关于', + order: undefined, + parent: undefined, + parents: undefined, + path: '/about', + show: true, + children: [], + }, + ]; + + const menus = await generateMenus(mockRoutes, mockRouter as any); + expect(menus).toEqual(expectedMenus); + }); + + it('includes additional meta properties in menu items', async () => { + const mockRoutesWithMeta = [ + { + meta: { icon: 'user-icon', order: 1, title: 'Profile' }, + name: 'profile', + path: '/profile', + }, + ] as RouteRecordRaw[]; + + const menus = await generateMenus(mockRoutesWithMeta, mockRouter as any); + expect(menus).toEqual([ + { + badge: undefined, + badgeType: undefined, + badgeVariants: undefined, + icon: 'user-icon', + name: 'Profile', + order: 1, + parent: undefined, + parents: undefined, + path: '/profile', + show: true, + children: [], + }, + ]); + }); + + it('handles dynamic route parameters correctly', async () => { + const mockRoutesWithParams = [ + { + meta: { icon: 'details-icon', title: 'User Details' }, + name: 'userDetails', + path: '/users/:userId', + }, + ] as RouteRecordRaw[]; + + const menus = await generateMenus(mockRoutesWithParams, mockRouter as any); + expect(menus).toEqual([ + { + badge: undefined, + badgeType: undefined, + badgeVariants: undefined, + icon: 'details-icon', + name: 'User Details', + order: undefined, + parent: undefined, + parents: undefined, + path: '/users/:userId', + show: true, + children: [], + }, + ]); + }); + + it('processes routes with redirects correctly', async () => { + const mockRoutesWithRedirect = [ + { + name: 'redirectedRoute', + path: '/old-path', + redirect: '/new-path', + }, + { + meta: { icon: 'path-icon', title: 'New Path' }, + name: 'newPath', + path: '/new-path', + }, + ] as RouteRecordRaw[]; + + const menus = await generateMenus( + mockRoutesWithRedirect, + mockRouter as any, + ); + expect(menus).toEqual([ + // Assuming your generateMenus function excludes redirect routes from the menu + { + badge: undefined, + badgeType: undefined, + badgeVariants: undefined, + icon: undefined, + name: 'redirectedRoute', + order: undefined, + parent: undefined, + parents: undefined, + path: '/old-path', + show: true, + children: [], + }, + { + badge: undefined, + badgeType: undefined, + badgeVariants: undefined, + icon: 'path-icon', + name: 'New Path', + order: undefined, + parent: undefined, + parents: undefined, + path: '/new-path', + show: true, + children: [], + }, + ]); + }); + + const routes: any = [ + { + meta: { order: 2, title: 'Home' }, + name: 'home', + path: '/', + }, + { + meta: { order: 1, title: 'About' }, + name: 'about', + path: '/about', + }, + ]; + + const router: Router = createRouter({ + history: createWebHistory(), + routes, + }); + + it('should generate menu list with correct order', async () => { + const menus = await generateMenus(routes, router); + const expectedMenus = [ + { + badge: undefined, + badgeType: undefined, + badgeVariants: undefined, + icon: undefined, + name: 'About', + order: 1, + parent: undefined, + parents: undefined, + path: '/about', + show: true, + children: [], + }, + { + badge: undefined, + badgeType: undefined, + badgeVariants: undefined, + icon: undefined, + name: 'Home', + order: 2, + parent: undefined, + parents: undefined, + path: '/', + show: true, + children: [], + }, + ]; + + expect(menus).toEqual(expectedMenus); + }); + + it('should handle empty routes', async () => { + const emptyRoutes: any[] = []; + const menus = await generateMenus(emptyRoutes, router); + expect(menus).toEqual([]); + }); +}); diff --git a/packages/utils/src/helpers/__tests__/generate-routes-frontend.test.ts b/packages/utils/src/helpers/__tests__/generate-routes-frontend.test.ts new file mode 100644 index 0000000..8e01853 --- /dev/null +++ b/packages/utils/src/helpers/__tests__/generate-routes-frontend.test.ts @@ -0,0 +1,105 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { describe, expect, it } from 'vitest'; + +import { + generateRoutesByFrontend, + hasAuthority, +} from '../generate-routes-frontend'; + +// Mock 路由数据 +const mockRoutes = [ + { + meta: { + authority: ['admin', 'user'], + hideInMenu: false, + }, + path: '/dashboard', + children: [ + { + path: '/dashboard/overview', + meta: { authority: ['admin'], hideInMenu: false }, + }, + { + path: '/dashboard/stats', + meta: { authority: ['user'], hideInMenu: true }, + }, + ], + }, + { + meta: { authority: ['admin'], hideInMenu: false }, + path: '/settings', + }, + { + meta: { hideInMenu: false }, + path: '/profile', + }, +] as RouteRecordRaw[]; + +describe('hasAuthority', () => { + it('should return true if there is no authority defined', () => { + expect(hasAuthority(mockRoutes[2], ['admin'])).toBe(true); + }); + + it('should return true if the user has the required authority', () => { + expect(hasAuthority(mockRoutes[0], ['admin'])).toBe(true); + }); + + it('should return false if the user does not have the required authority', () => { + expect(hasAuthority(mockRoutes[1], ['user'])).toBe(false); + }); +}); + +describe('generateRoutesByFrontend', () => { + it('should handle routes without children', async () => { + const generatedRoutes = await generateRoutesByFrontend(mockRoutes, [ + 'user', + ]); + expect(generatedRoutes).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + path: '/profile', // This route has no children and should be included + }), + ]), + ); + }); + + it('should handle empty roles array', async () => { + const generatedRoutes = await generateRoutesByFrontend(mockRoutes, []); + expect(generatedRoutes).toEqual( + expect.arrayContaining([ + // Only routes without authority should be included + expect.objectContaining({ + path: '/profile', + }), + ]), + ); + expect(generatedRoutes).not.toEqual( + expect.arrayContaining([ + expect.objectContaining({ + path: '/dashboard', + }), + expect.objectContaining({ + path: '/settings', + }), + ]), + ); + }); + + it('should handle missing meta fields', async () => { + const routesWithMissingMeta = [ + { path: '/path1' }, // No meta + { meta: {}, path: '/path2' }, // Empty meta + { meta: { authority: ['admin'] }, path: '/path3' }, // Only authority + ]; + const generatedRoutes = await generateRoutesByFrontend( + routesWithMissingMeta as RouteRecordRaw[], + ['admin'], + ); + expect(generatedRoutes).toEqual([ + { path: '/path1' }, + { meta: {}, path: '/path2' }, + { meta: { authority: ['admin'] }, path: '/path3' }, + ]); + }); +}); diff --git a/packages/utils/src/helpers/__tests__/merge-route-modules.test.ts b/packages/utils/src/helpers/__tests__/merge-route-modules.test.ts new file mode 100644 index 0000000..3615556 --- /dev/null +++ b/packages/utils/src/helpers/__tests__/merge-route-modules.test.ts @@ -0,0 +1,68 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import type { RouteModuleType } from '../merge-route-modules'; + +import { describe, expect, it } from 'vitest'; + +import { mergeRouteModules } from '../merge-route-modules'; + +describe('mergeRouteModules', () => { + it('should merge route modules correctly', () => { + const routeModules: Record = { + './dynamic-routes/about.ts': { + default: [ + { + component: () => Promise.resolve({ template: '
    About
    ' }), + name: 'About', + path: '/about', + }, + ], + }, + './dynamic-routes/home.ts': { + default: [ + { + component: () => Promise.resolve({ template: '
    Home
    ' }), + name: 'Home', + path: '/', + }, + ], + }, + }; + + const expectedRoutes: RouteRecordRaw[] = [ + { + component: expect.any(Function), + name: 'About', + path: '/about', + }, + { + component: expect.any(Function), + name: 'Home', + path: '/', + }, + ]; + + const mergedRoutes = mergeRouteModules(routeModules); + expect(mergedRoutes).toEqual(expectedRoutes); + }); + + it('should handle empty modules', () => { + const routeModules: Record = {}; + const expectedRoutes: RouteRecordRaw[] = []; + + const mergedRoutes = mergeRouteModules(routeModules); + expect(mergedRoutes).toEqual(expectedRoutes); + }); + + it('should handle modules with no default export', () => { + const routeModules: Record = { + './dynamic-routes/empty.ts': { + default: [], + }, + }; + const expectedRoutes: RouteRecordRaw[] = []; + + const mergedRoutes = mergeRouteModules(routeModules); + expect(mergedRoutes).toEqual(expectedRoutes); + }); +}); diff --git a/packages/utils/src/helpers/enum-options.ts b/packages/utils/src/helpers/enum-options.ts new file mode 100644 index 0000000..5ac4c5a --- /dev/null +++ b/packages/utils/src/helpers/enum-options.ts @@ -0,0 +1,47 @@ +/** + * @author dap + * @description 枚举选项 + */ + +/** + * 定义options类型 + */ +export interface EnumsOption { + /** + * 枚举名称 建议使用全大写字母_ + */ + enumName: string; + /** + * option的标签 + */ + label: string; + /** + * option的值 + */ + value: boolean | number | string; +} + +export type EnumResult = { + [key in T[number]['enumName']]: Extract< + T[number], + { enumName: key } + >['value']; +}; + +/** + * 将options转为枚举 + * 注意自定义的options需要加上as const作为常量处理 + * 详见: packages\utils\src\helpers\__tests__\enum-options.test.ts + * @param options 枚举选项 + * @returns 转枚举 + */ +export function optionsToEnum( + options: T, +): EnumResult { + type K = T[number]['enumName']; + const result = {} as EnumResult; + options.forEach((item) => { + result[item.enumName as K] = item.value; + }); + return result; +} diff --git a/packages/utils/src/helpers/find-menu-by-path.ts b/packages/utils/src/helpers/find-menu-by-path.ts new file mode 100644 index 0000000..747cc7f --- /dev/null +++ b/packages/utils/src/helpers/find-menu-by-path.ts @@ -0,0 +1,37 @@ +import type { MenuRecordRaw } from '@vben-core/typings'; + +function findMenuByPath( + list: MenuRecordRaw[], + path?: string, +): MenuRecordRaw | null { + for (const menu of list) { + if (menu.path === path) { + return menu; + } + const findMenu = menu.children && findMenuByPath(menu.children, path); + if (findMenu) { + return findMenu; + } + } + return null; +} + +/** + * 查找根菜单 + * @param menus + * @param path + */ +function findRootMenuByPath(menus: MenuRecordRaw[], path?: string, level = 0) { + const findMenu = findMenuByPath(menus, path); + const rootMenuPath = findMenu?.parents?.[level]; + const rootMenu = rootMenuPath + ? menus.find((item) => item.path === rootMenuPath) + : undefined; + return { + findMenu, + rootMenu, + rootMenuPath, + }; +} + +export { findMenuByPath, findRootMenuByPath }; diff --git a/packages/utils/src/helpers/generate-menus.ts b/packages/utils/src/helpers/generate-menus.ts new file mode 100644 index 0000000..48c30fd --- /dev/null +++ b/packages/utils/src/helpers/generate-menus.ts @@ -0,0 +1,80 @@ +import type { ExRouteRecordRaw, MenuRecordRaw } from '@vben-core/typings'; +import type { Router, RouteRecordRaw } from 'vue-router'; + +import { filterTree, mapTree } from '@vben-core/shared/utils'; + +/** + * 根据 routes 生成菜单列表 + * @param routes + */ +async function generateMenus( + routes: RouteRecordRaw[], + router: Router, +): Promise { + // 将路由列表转换为一个以 name 为键的对象映射 + // 获取所有router最终的path及name + const finalRoutesMap: { [key: string]: string } = Object.fromEntries( + router.getRoutes().map(({ name, path }) => [name, path]), + ); + + let menus = mapTree(routes, (route) => { + // 路由表的路径写法有多种,这里从router获取到最终的path并赋值 + const path = finalRoutesMap[route.name as string] ?? route.path; + + // 转换为菜单结构 + // const path = matchRoute?.path ?? route.path; + const { meta, name: routeName, redirect, children } = route; + const { + activeIcon, + badge, + badgeType, + badgeVariants, + hideChildrenInMenu = false, + icon, + link, + order, + title = '', + } = meta || {}; + + const name = (title || routeName || '') as string; + + // 隐藏子菜单 + const resultChildren = hideChildrenInMenu + ? [] + : (children as MenuRecordRaw[]); + + // 将菜单的所有父级和父级菜单记录到菜单项内 + if (resultChildren && resultChildren.length > 0) { + resultChildren.forEach((child) => { + child.parents = [...(route.parents || []), path]; + child.parent = path; + }); + } + // 隐藏子菜单 + const resultPath = hideChildrenInMenu ? redirect || path : link || path; + return { + activeIcon, + badge, + badgeType, + badgeVariants, + icon, + name, + order, + parent: route.parent, + parents: route.parents, + path: resultPath as string, + show: !route?.meta?.hideInMenu, + children: resultChildren || [], + }; + }); + + // 对菜单进行排序 + menus = menus.sort((a, b) => (a.order || 999) - (b.order || 999)); + + const finalMenus = filterTree(menus, (menu) => { + return !!menu.show; + }); + return finalMenus; +} + +export { generateMenus }; diff --git a/packages/utils/src/helpers/generate-routes-backend.ts b/packages/utils/src/helpers/generate-routes-backend.ts new file mode 100644 index 0000000..f64a69a --- /dev/null +++ b/packages/utils/src/helpers/generate-routes-backend.ts @@ -0,0 +1,87 @@ +import type { + ComponentRecordType, + GenerateMenuAndRoutesOptions, + RouteRecordStringComponent, +} from '@vben-core/typings'; +import type { RouteRecordRaw } from 'vue-router'; + +import { mapTree } from '@vben-core/shared/utils'; + +/** + * 动态生成路由 - 后端方式 + */ +async function generateRoutesByBackend( + options: GenerateMenuAndRoutesOptions, +): Promise { + const { fetchMenuListAsync, layoutMap = {}, pageMap = {} } = options; + + try { + const menuRoutes = await fetchMenuListAsync?.(); + if (!menuRoutes) { + return []; + } + + const normalizePageMap: ComponentRecordType = {}; + + for (const [key, value] of Object.entries(pageMap)) { + normalizePageMap[normalizeViewPath(key)] = value; + } + + const routes = convertRoutes(menuRoutes, layoutMap, normalizePageMap); + + return routes; + } catch (error) { + console.error(error); + return []; + } +} + +function convertRoutes( + routes: RouteRecordStringComponent[], + layoutMap: ComponentRecordType, + pageMap: ComponentRecordType, +): RouteRecordRaw[] { + return mapTree(routes, (node) => { + const route = node as unknown as RouteRecordRaw; + const { component, name } = node; + + if (!name) { + console.error('route name is required', route); + } + + // layout转换 + if (component && layoutMap[component]) { + route.component = layoutMap[component]; + // 页面组件转换 + } else if (component) { + const normalizePath = normalizeViewPath(component); + route.component = + pageMap[ + normalizePath.endsWith('.vue') + ? normalizePath + : `${normalizePath}.vue` + ]; + if (!route.component) { + console.error(`未找到对应组件: /views${component}.vue`); + // 默认为404页面 + route.component = layoutMap.NotFoundComponent; + } + } + + return route; + }); +} + +function normalizeViewPath(path: string): string { + // 去除相对路径前缀 + const normalizedPath = path.replace(/^(\.\/|\.\.\/)+/, ''); + + // 确保路径以 '/' 开头 + const viewPath = normalizedPath.startsWith('/') + ? normalizedPath + : `/${normalizedPath}`; + + // 这里耦合了vben-admin的目录结构 + return viewPath.replace(/^\/views/, ''); +} +export { generateRoutesByBackend }; diff --git a/packages/utils/src/helpers/generate-routes-frontend.ts b/packages/utils/src/helpers/generate-routes-frontend.ts new file mode 100644 index 0000000..dafc8a7 --- /dev/null +++ b/packages/utils/src/helpers/generate-routes-frontend.ts @@ -0,0 +1,58 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { filterTree, mapTree } from '@vben-core/shared/utils'; + +/** + * 动态生成路由 - 前端方式 + */ +async function generateRoutesByFrontend( + routes: RouteRecordRaw[], + roles: string[], + forbiddenComponent?: RouteRecordRaw['component'], +): Promise { + // 根据角色标识过滤路由表,判断当前用户是否拥有指定权限 + const finalRoutes = filterTree(routes, (route) => { + return hasAuthority(route, roles); + }); + + if (!forbiddenComponent) { + return finalRoutes; + } + + // 如果有禁止访问的页面,将禁止访问的页面替换为403页面 + return mapTree(finalRoutes, (route) => { + if (menuHasVisibleWithForbidden(route)) { + route.component = forbiddenComponent; + } + return route; + }); +} + +/** + * 判断路由是否有权限访问 + * @param route + * @param access + */ +function hasAuthority(route: RouteRecordRaw, access: string[]) { + const authority = route.meta?.authority; + if (!authority) { + return true; + } + const canAccess = access.some((value) => authority.includes(value)); + + return canAccess || (!canAccess && menuHasVisibleWithForbidden(route)); +} + +/** + * 判断路由是否在菜单中显示,但是访问会被重定向到403 + * @param route + */ +function menuHasVisibleWithForbidden(route: RouteRecordRaw) { + return ( + !!route.meta?.authority && + Reflect.has(route.meta || {}, 'menuVisibleWithForbidden') && + !!route.meta?.menuVisibleWithForbidden + ); +} + +export { generateRoutesByFrontend, hasAuthority }; diff --git a/packages/utils/src/helpers/get-popup-container.ts b/packages/utils/src/helpers/get-popup-container.ts new file mode 100644 index 0000000..ccd48ec --- /dev/null +++ b/packages/utils/src/helpers/get-popup-container.ts @@ -0,0 +1,34 @@ +/** + * Returns the parent node of the given element or the document body if the element is not provided.it + */ +export function getPopupContainer(node?: HTMLElement): HTMLElement { + return (node?.parentNode as HTMLElement) ?? document.body; +} + +/** + * VxeTable专用弹窗层 + * 解决问题: https://gitee.com/dapppp/ruoyi-plus-vben5/issues/IB1DM3 + * 单表格用法跟上面getPopupContainer一样 + * 一个页面(body下)有多个表格元素 必须先指定ID & ID参数传入该函数 + * + * getVxePopupContainer="(node) => getVxePopupContainer(node, 'xxx')" + * @param _node 触发的元素 + * @param id 表格唯一id 当页面(该窗口)有>=两个表格 必须提供ID + * @returns 挂载节点 + */ +export function getVxePopupContainer( + _node?: HTMLElement, + id?: string, +): HTMLElement { + let selector = 'div.vxe-table--body-wrapper.body--wrapper'; + if (id) { + selector = `div#${id} ${selector}`; + } + // 挂载到vxe-table的滚动区域 + const vxeTableContainerNode = document.querySelector(selector); + if (!vxeTableContainerNode) { + console.warn('无法找到vxe-table元素, 将会挂载到body.'); + return document.body; + } + return vxeTableContainerNode as HTMLElement; +} diff --git a/packages/utils/src/helpers/index.ts b/packages/utils/src/helpers/index.ts new file mode 100644 index 0000000..1733d1a --- /dev/null +++ b/packages/utils/src/helpers/index.ts @@ -0,0 +1,14 @@ +export * from './enum-options'; +export * from './find-menu-by-path'; +export * from './generate-menus'; +export * from './generate-routes-backend'; +export * from './generate-routes-frontend'; +export * from './get-popup-container'; +export * from './merge-route-modules'; +export * from './mitt'; +export * from './request'; +export * from './reset-routes'; +export * from './safe'; +export * from './tree'; +export * from './unmount-global-loading'; +export * from './uuid'; diff --git a/packages/utils/src/helpers/merge-route-modules.ts b/packages/utils/src/helpers/merge-route-modules.ts new file mode 100644 index 0000000..53e21f3 --- /dev/null +++ b/packages/utils/src/helpers/merge-route-modules.ts @@ -0,0 +1,28 @@ +import type { RouteRecordRaw } from 'vue-router'; + +// 定义模块类型 +interface RouteModuleType { + default: RouteRecordRaw[]; +} + +/** + * 合并动态路由模块的默认导出 + * @param routeModules 动态导入的路由模块对象 + * @returns 合并后的路由配置数组 + */ +function mergeRouteModules( + routeModules: Record, +): RouteRecordRaw[] { + const mergedRoutes: RouteRecordRaw[] = []; + + for (const routeModule of Object.values(routeModules)) { + const moduleRoutes = (routeModule as RouteModuleType)?.default ?? []; + mergedRoutes.push(...moduleRoutes); + } + + return mergedRoutes; +} + +export { mergeRouteModules }; + +export type { RouteModuleType }; diff --git a/packages/utils/src/helpers/mitt.ts b/packages/utils/src/helpers/mitt.ts new file mode 100644 index 0000000..52be313 --- /dev/null +++ b/packages/utils/src/helpers/mitt.ts @@ -0,0 +1,135 @@ +/** + * copy to https://github.com/developit/mitt + * Expand clear method + */ +export type EventType = string | symbol; + +// An event handler can take an optional event argument +// and should not return a value +export type Handler = (event: T) => void; +export type WildcardHandler> = ( + type: keyof T, + event: T[keyof T], +) => void; + +// An array of all currently registered event handlers for a type +export type EventHandlerList = Array>; +export type WildCardEventHandlerList> = Array< + WildcardHandler +>; + +// A map of event types and their corresponding event handlers. +export type EventHandlerMap> = Map< + '*' | keyof Events, + EventHandlerList | WildCardEventHandlerList +>; + +export interface Emitter> { + all: EventHandlerMap; + + clear(): void; + emit( + type: undefined extends Events[Key] ? Key : never, + ): void; + + emit(type: Key, event: Events[Key]): void; + off(type: '*', handler: WildcardHandler): void; + + off( + type: Key, + handler?: Handler, + ): void; + on(type: '*', handler: WildcardHandler): void; + on(type: Key, handler: Handler): void; +} + +/** + * Mitt: Tiny (~200b) functional event emitter / pubsub. + * @name mitt + * @returns {Mitt} any + */ +export function mitt>( + all?: EventHandlerMap, +): Emitter { + type GenericEventHandler = + | Handler + | WildcardHandler; + all = all || new Map(); + + return { + /** + * A Map of event names to registered handler functions. + */ + all, + + /** + * Clear all + */ + clear() { + this.all.clear(); + }, + + /** + * Invoke all handlers for the given type. + * If present, `'*'` handlers are invoked after type-matched handlers. + * + * Note: Manually firing '*' handlers is not supported. + * + * @param {string|symbol} type The event type to invoke + * @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler + * @memberOf mitt + */ + emit(type: Key, evt?: Events[Key]) { + let handlers = all?.get(type); + if (handlers) { + [...(handlers as EventHandlerList)].forEach( + (handler) => { + handler(evt as Events[Key]); + }, + ); + } + + handlers = all?.get('*'); + if (handlers) { + [...(handlers as WildCardEventHandlerList)].forEach( + (handler) => { + handler(type, evt as Events[Key]); + }, + ); + } + }, + + /** + * Remove an event handler for the given type. + * If `handler` is omitted, all handlers of the given type are removed. + * @param {string|symbol} type Type of event to unregister `handler` from (`'*'` to remove a wildcard handler) + * @param {Function} [handler] Handler function to remove + * @memberOf mitt + */ + off(type: Key, handler?: GenericEventHandler) { + const handlers: Array | undefined = all?.get(type); + if (handlers) { + if (handler) { + handlers.splice(handlers.indexOf(handler) >>> 0, 1); + } else { + all?.set(type, []); + } + } + }, + + /** + * Register an event handler for the given type. + * @param {string|symbol} type Type of event to listen for, or `'*'` for all events + * @param {Function} handler Function to call in response to given event + * @memberOf mitt + */ + on(type: Key, handler: GenericEventHandler) { + const handlers: Array | undefined = all?.get(type); + if (handlers) { + handlers.push(handler); + } else { + all?.set(type, [handler] as EventHandlerList); + } + }, + }; +} diff --git a/packages/utils/src/helpers/request.ts b/packages/utils/src/helpers/request.ts new file mode 100644 index 0000000..b29e294 --- /dev/null +++ b/packages/utils/src/helpers/request.ts @@ -0,0 +1,24 @@ +/** + * 一些发送请求 需要用到的工具 + */ + +/** + * Add the object as a parameter to the URL + * @param baseUrl url + * @param obj + * @returns {string} + * eg: + * let obj = {a: '3', b: '4'} + * setObjToUrlParams('www.baidu.com', obj) + * ==>www.baidu.com?a=3&b=4 + */ +export function setObjToUrlParams(baseUrl: string, obj: any): string { + let parameters = ''; + for (const key in obj) { + parameters += `${key}=${encodeURIComponent(obj[key])}&`; + } + parameters = parameters.replace(/&$/, ''); + return /\?$/.test(baseUrl) + ? baseUrl + parameters + : baseUrl.replace(/\/?$/, '?') + parameters; +} diff --git a/packages/utils/src/helpers/reset-routes.ts b/packages/utils/src/helpers/reset-routes.ts new file mode 100644 index 0000000..0d53a00 --- /dev/null +++ b/packages/utils/src/helpers/reset-routes.ts @@ -0,0 +1,31 @@ +import type { Router, RouteRecordName, RouteRecordRaw } from 'vue-router'; + +import { traverseTreeValues } from '@vben-core/shared/utils'; + +/** + * @zh_CN 重置所有路由,如有指定白名单除外 + */ +export function resetStaticRoutes(router: Router, routes: RouteRecordRaw[]) { + // 获取静态路由所有节点包含子节点的 name,并排除不存在 name 字段的路由 + const staticRouteNames = traverseTreeValues< + RouteRecordRaw, + RouteRecordName | undefined + >(routes, (route) => { + // 这些路由需要指定 name,防止在路由重置时,不能删除没有指定 name 的路由 + if (!route.name) { + console.warn( + `The route with the path ${route.path} needs to have the field name specified.`, + ); + } + return route.name; + }); + + const { getRoutes, hasRoute, removeRoute } = router; + const allRoutes = getRoutes(); + allRoutes.forEach(({ name }) => { + // 存在于路由表且非白名单才需要删除 + if (name && !staticRouteNames.includes(name) && hasRoute(name)) { + removeRoute(name); + } + }); +} diff --git a/packages/utils/src/helpers/safe.ts b/packages/utils/src/helpers/safe.ts new file mode 100644 index 0000000..ad31337 --- /dev/null +++ b/packages/utils/src/helpers/safe.ts @@ -0,0 +1,10 @@ +/** + * 跟后台逻辑一致 + * Number.isSafeInteger形参只能为Number类型 其他的直接返回false + * @param str 数字 + * @returns 安全数内返回number类型 否则返回原字符串 + */ +export function safeParseNumber(str: string): number | string { + const num = Number(str); + return Number.isSafeInteger(num) ? num : str; +} diff --git a/packages/utils/src/helpers/tree.ts b/packages/utils/src/helpers/tree.ts new file mode 100644 index 0000000..9b0f4b7 --- /dev/null +++ b/packages/utils/src/helpers/tree.ts @@ -0,0 +1,400 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +interface TreeHelperConfig { + children: string; + id: string; + pid: string; +} + +type Fn = (node: any, parentNode?: any) => any; + +// 默认配置 +const DEFAULT_CONFIG: TreeHelperConfig = { + id: 'id', + pid: 'parentId', + children: 'children', +}; + +// 获取配置。 Object.assign 从一个或多个源对象复制到目标对象 +const getConfig = (config: Partial) => + Object.assign({}, DEFAULT_CONFIG, config); + +// tree from list +// 列表中的树 +export function listToTree( + list: any[], + config: Partial = {}, +): T[] { + const conf = getConfig(config) as TreeHelperConfig; + const nodeMap = new Map(); + const result: T[] = []; + const { id, pid, children } = conf; + + for (const node of list) { + node[children] = node[children] || []; + nodeMap.set(node[id], node); + } + for (const node of list) { + const parent = nodeMap.get(node[pid]); + (parent ? parent[children] : result).push(node); + } + return result; +} + +export function treeToList( + tree: any, + config: Partial = {}, +): T { + config = getConfig(config); + const { children } = config; + const result: any = [...tree]; + for (let i = 0; i < result.length; i++) { + if (!result[i][children!]) continue; + result.splice(i + 1, 0, ...result[i][children!]); + } + return result; +} + +export function findNode( + tree: any, + func: Fn, + config: Partial = {}, +): null | T { + config = getConfig(config); + const { children } = config; + const list = [...tree]; + for (const node of list) { + if (func(node)) return node; + node[children!] && list.push(...node[children!]); + } + return null; +} + +export function findNodeAll( + tree: any, + func: Fn, + config: Partial = {}, +): T[] { + config = getConfig(config); + const { children } = config; + const list = [...tree]; + const result: T[] = []; + for (const node of list) { + func(node) && result.push(node); + node[children!] && list.push(...node[children!]); + } + return result; +} + +export function findPath( + tree: any, + func: Fn, + config: Partial = {}, +): null | T | T[] { + config = getConfig(config); + const path: T[] = []; + const list = [...tree]; + const visitedSet = new Set(); + const { children } = config; + while (list.length > 0) { + const node = list[0]; + if (visitedSet.has(node)) { + path.pop(); + list.shift(); + } else { + visitedSet.add(node); + node[children!] && list.unshift(...node[children!]); + path.push(node); + if (func(node)) { + return path; + } + } + } + return null; +} + +export function findPathAll( + tree: any, + func: Fn, + config: Partial = {}, +) { + config = getConfig(config); + const path: any[] = []; + const list = [...tree]; + const result: any[] = []; + const { children } = config; + const visitedSet = new Set(); + while (list.length > 0) { + const node = list[0]; + if (visitedSet.has(node)) { + path.pop(); + list.shift(); + } else { + visitedSet.add(node); + node[children!] && list.unshift(...node[children!]); + path.push(node); + func(node) && result.push([...path]); + } + } + return result; +} + +export function filter( + tree: T[], + func: (n: T) => boolean, + // Partial 将 T 中的所有属性设为可选 + config: Partial = {}, +): T[] { + // 获取配置 + config = getConfig(config); + const children = config.children as string; + + function listFilter(list: T[]) { + return list + .map((node: any) => ({ ...node })) + .filter((node) => { + // 递归调用 对含有children项 进行再次调用自身函数 listFilter + node[children] = node[children] && listFilter(node[children]); + // 执行传入的回调 func 进行过滤 + return func(node) || (node[children] && node[children].length > 0); + }); + } + + return listFilter(tree); +} + +export function forEach( + tree: T[], + func: (n: T) => any, + config: Partial = {}, +): void { + config = getConfig(config); + const list: any[] = [...tree]; + const { children } = config; + for (let i = 0; i < list.length; i++) { + // func 返回true就终止遍历,避免大量节点场景下无意义循环,引起浏览器卡顿 + if (func(list[i])) { + return; + } + children && + list[i][children] && + list.splice(i + 1, 0, ...list[i][children]); + } +} + +/** + * @description: Extract tree specified structure + * @description: 提取树指定结构 + */ +export function treeMap( + treeData: T[], + opt: { children?: string; conversion: Fn }, +): T[] { + return treeData.map((item) => treeMapEach(item, opt)); +} + +/** + * @description: Extract tree specified structure + * @description: 提取树指定结构 + */ +export function treeMapEach( + data: any, + { conversion, children = 'children' }: { children?: string; conversion: Fn }, +) { + const haveChildren = + Array.isArray(data[children]) && data[children].length > 0; + const conversionData = conversion(data) || {}; + return haveChildren + ? { + ...conversionData, + [children]: data[children].map((i: number) => + treeMapEach(i, { + children, + conversion, + }), + ), + } + : { + ...conversionData, + }; +} + +/** + * 递归遍历树结构 + * @param treeDatas 树 + * @param callBack 回调 + * @param parentNode 父节点 + */ +export function eachTree(treeDatas: any[], callBack: Fn, parentNode = {}) { + treeDatas.forEach((element) => { + const newNode = callBack(element, parentNode) || element; + if (element.children) { + eachTree(element.children, callBack, newNode); + } + }); +} + +// 如果节点的children为空, 则删除children属性 +export function removeEmptyChildren(data: any[], childrenField = 'children') { + data.forEach((item) => { + if (!item[childrenField]) { + return; + } + if (item[childrenField].length > 0) { + removeEmptyChildren(item[childrenField]); + } else { + Reflect.deleteProperty(item, childrenField); + } + }); +} + +// eslint-disable-next-line jsdoc/require-returns-check +/** + * + * 添加全名 如 祖先节点-父节点-子节点 + * @param treeData 已经是tree数据 + * @param labelName 标签的字段名称 + * @param splitStr 分隔符 + * @returns void 无返回值 会修改原始数据 + */ +export function addFullName( + treeData: any[], + labelName = 'label', + splitStr = '-', +) { + function addFullNameProperty(node: any, parentNames: any[] = []) { + const fullNameParts = [...parentNames, node[labelName]]; + node.fullName = fullNameParts.join(splitStr); + if (node.children && node.children.length > 0) { + node.children.forEach((childNode: any) => { + addFullNameProperty(childNode, fullNameParts); + }); + } + } + + treeData.forEach((item: any) => { + addFullNameProperty(item); + }); +} + +/** + * https://blog.csdn.net/Web_J/article/details/129281329 + * 给出节点nodeId 找到所有父节点ID + * @param treeList 树形结构list + * @param nodeId 要寻找的节点ID + * @param config config + * @returns 父节点ID数组 + */ +export function findParentsIds( + treeList: any[], + nodeId: number, + config: Partial = {}, +) { + const conf = getConfig(config) as TreeHelperConfig; + const { id, children } = conf; + + // 用于存储所有父节点ID的数组 + const parentIds: number[] = []; + + function traverse(node: any, nodeId: number) { + if (node[id] === nodeId) { + return true; + } + if (node[children]) { + // 如果当前节点有子节点,则继续遍历子节点 + for (const childNode of node[children]) { + if (traverse(childNode, nodeId)) { + // 如果在子节点中找到了子节点的父节点,则将当前节点的ID添加到父节点ID数组中,并返回true表示已经找到了子节点 + parentIds.push(node[id]); + return true; + } + } + } + return false; + } + + for (const node of treeList) { + if (traverse(node, nodeId)) { + // 如果在当前节点的子树中找到了子节点的父节点,则直接退出循环 + break; + } + } + + return parentIds.sort(); +} + +/** + * 给出节点数组 找到所有父节点ID + * @param treeList 树形结构list + * @param nodeIds 要寻找的节点ID list + * @param config config + * @returns 父节点ID数组 + */ +export function findGroupParentIds( + treeList: any[], + nodeIds: number[], + config: Partial = {}, +) { + // 用于存储所有父节点ID的Set 主要为了去重 + const parentIds = new Set(); + + nodeIds.forEach((nodeId) => { + findParentsIds(treeList, nodeId, config).forEach((parentId) => { + parentIds.add(parentId); + }); + }); + + return [...parentIds].sort(); +} + +/** + * 找到所有ID 返回数组 + * @param treeList list + * @param config + * @returns ID数组 + */ +export function findAllIds( + treeList: any[], + config: Partial = DEFAULT_CONFIG, +) { + const conf = getConfig(config) as TreeHelperConfig; + const { id, children } = conf; + const ids: number[] = []; + + treeList.forEach((item) => { + if (item[children]) { + const tempIds = findAllIds(item[children], config); + ids.push(...tempIds); + } + ids.push(item[id]); + }); + + return [...ids].sort(); +} + +/** + * @description 这里抄的filterByLevel函数 + * @description 主要用于获取指定层级的节点数组 + */ +export function findIdsByLevel( + level = 1, + list?: any[], + config: Partial = DEFAULT_CONFIG, + currentLevel = 1, +) { + if (!level) { + return []; + } + const res: (number | string)[] = []; + const data = list || []; + for (const item of data) { + const { id: keyField, children: childrenField } = config; + const key = keyField ? item[keyField] : ''; + const children = childrenField ? item[childrenField] : []; + res.push(key); + if (children && children.length > 0 && currentLevel < level) { + currentLevel += 1; + res.push(...findIdsByLevel(level, children, config, currentLevel)); + } + } + return res as number[] | string[]; +} diff --git a/packages/utils/src/helpers/unmount-global-loading.ts b/packages/utils/src/helpers/unmount-global-loading.ts new file mode 100644 index 0000000..10b88ea --- /dev/null +++ b/packages/utils/src/helpers/unmount-global-loading.ts @@ -0,0 +1,31 @@ +/** + * 移除并销毁loading + * 放在这里是而不是放在 index.html 的app标签内,是因为这样比较不会生硬,渲染过快可能会有闪烁 + * 通过先添加css动画隐藏,在动画结束后在移除loading节点来改善体验 + * 不好的地方是会增加一些代码量 + * 自定义loading可以见:https://doc.vben.pro/guide/in-depth/loading.html + */ +export function unmountGlobalLoading() { + // 查找全局 loading 元素 + const loadingElement = document.querySelector('#__app-loading__'); + + if (loadingElement) { + // 添加隐藏类,触发过渡动画 + loadingElement.classList.add('hidden'); + + // 查找所有需要移除的注入 loading 元素 + const injectLoadingElements = document.querySelectorAll( + '[data-app-loading^="inject"]', + ); + + // 当过渡动画结束时,移除 loading 元素和所有注入的 loading 元素 + loadingElement.addEventListener( + 'transitionend', + () => { + loadingElement.remove(); // 移除 loading 元素 + injectLoadingElements.forEach((el) => el.remove()); // 移除所有注入的 loading 元素 + }, + { once: true }, + ); // 确保事件只触发一次 + } +} diff --git a/packages/utils/src/helpers/uuid.ts b/packages/utils/src/helpers/uuid.ts new file mode 100644 index 0000000..81c49a0 --- /dev/null +++ b/packages/utils/src/helpers/uuid.ts @@ -0,0 +1,42 @@ +const hexList: string[] = []; +for (let i = 0; i <= 15; i++) { + hexList[i] = i.toString(16); +} + +export function buildUUID(): string { + let uuid = ''; + for (let i = 1; i <= 36; i++) { + switch (i) { + case 9: + case 14: + case 19: + case 24: { + uuid += '-'; + + break; + } + case 15: { + uuid += 4; + + break; + } + case 20: { + uuid += hexList[(Math.random() * 4) | 8]; + + break; + } + default: { + uuid += hexList[Math.trunc(Math.random() * 16)]; + } + } + } + return uuid.replaceAll('-', ''); +} + +let unique = 0; +export function buildShortUUID(prefix = ''): string { + const time = Date.now(); + const random = Math.floor(Math.random() * 1_000_000_000); + unique++; + return `${prefix}_${random}${unique}${String(time)}`; +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts new file mode 100644 index 0000000..dd84fa7 --- /dev/null +++ b/packages/utils/src/index.ts @@ -0,0 +1,5 @@ +export * from './helpers'; +export * from '@vben-core/shared/cache'; +export * from '@vben-core/shared/color'; +export * from '@vben-core/shared/utils'; +export { fileTypeFromBlob } from 'file-type'; diff --git a/packages/utils/tsconfig.json b/packages/utils/tsconfig.json new file mode 100644 index 0000000..255148a --- /dev/null +++ b/packages/utils/tsconfig.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/library.json", + "compilerOptions": { + "types": ["@vben-core/typings/vue-router"] + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..938f786 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,185 @@ +packages: + - internal/* + - internal/lint-configs/* + - packages/* + - packages/@core/base/* + - packages/@core/ui-kit/* + - packages/@core/forward/* + - packages/@core/* + - packages/effects/* + - packages/business/* + - apps/* + - scripts/* + - docs + - playground +catalog: + '@ast-grep/napi': ^0.32.3 + '@changesets/changelog-github': ^0.5.0 + '@changesets/cli': ^2.27.12 + '@changesets/git': ^3.0.2 + '@clack/prompts': ^0.9.1 + '@commitlint/cli': ^19.7.1 + '@commitlint/config-conventional': ^19.7.1 + '@ctrl/tinycolor': ^4.1.0 + '@eslint/js': ^9.19.0 + '@faker-js/faker': ^9.4.0 + '@iconify/json': ^2.2.302 + '@iconify/tailwind': ^1.2.0 + '@iconify/vue': ^4.3.0 + '@intlify/core-base': ^11.1.0 + '@intlify/unplugin-vue-i18n': ^6.0.3 + '@jspm/generator': ^2.4.2 + '@manypkg/get-packages': ^2.2.2 + '@nolebase/vitepress-plugin-git-changelog': ^2.12.1 + '@playwright/test': ^1.50.1 + '@pnpm/workspace.read-manifest': ^1000.0.2 + '@stylistic/stylelint-plugin': ^3.1.1 + '@tailwindcss/nesting': 0.0.0-insiders.565cd3e + '@tailwindcss/typography': ^0.5.16 + '@tanstack/vue-query': ^5.65.0 + '@tanstack/vue-store': ^0.7.0 + '@types/archiver': ^6.0.3 + '@types/eslint': ^9.6.1 + '@types/html-minifier-terser': ^7.0.2 + '@types/jsonwebtoken': ^9.0.8 + '@types/lodash.clonedeep': ^4.5.9 + '@types/lodash.get': ^4.4.9 + '@types/lodash.isequal': ^4.5.8 + '@types/node': ^22.13.1 + '@types/nprogress': ^0.2.3 + '@types/postcss-import': ^14.0.3 + '@types/qrcode': ^1.5.5 + '@types/sortablejs': ^1.15.8 + '@typescript-eslint/eslint-plugin': ^8.23.0 + '@typescript-eslint/parser': ^8.23.0 + '@vee-validate/zod': ^4.15.0 + '@vite-pwa/vitepress': ^0.5.3 + '@vitejs/plugin-vue': ^5.2.1 + '@vitejs/plugin-vue-jsx': ^4.1.1 + '@vue/reactivity': ^3.5.13 + '@vue/shared': ^3.5.13 + '@vue/test-utils': ^2.4.6 + '@vueuse/core': ^12.5.0 + '@vueuse/integrations': ^12.5.0 + ant-design-vue: ^4.2.6 + archiver: ^7.0.1 + autoprefixer: ^10.4.20 + axios: ^1.7.9 + axios-mock-adapter: ^2.1.0 + cac: ^6.7.14 + chalk: ^5.4.1 + cheerio: ^1.0.0 + circular-dependency-scanner: ^2.3.0 + class-variance-authority: ^0.7.1 + clsx: ^2.1.1 + commitlint-plugin-function-rules: ^4.0.1 + consola: ^3.4.0 + cross-env: ^7.0.3 + cspell: 8.17.2 + cssnano: ^7.0.6 + cz-git: ^1.11.0 + czg: ^1.11.0 + dayjs: ^1.11.13 + defu: ^6.1.4 + depcheck: ^1.4.7 + dotenv: ^16.4.7 + echarts: ^5.6.0 + element-plus: ^2.9.3 + eslint: ^9.19.0 + eslint-config-turbo: ^2.4.0 + eslint-plugin-command: ^0.2.7 + eslint-plugin-eslint-comments: ^3.2.0 + eslint-plugin-import-x: ^4.6.1 + eslint-plugin-jsdoc: ^50.6.3 + eslint-plugin-jsonc: ^2.19.1 + eslint-plugin-n: ^17.15.1 + eslint-plugin-no-only-tests: ^3.3.0 + eslint-plugin-perfectionist: ^4.8.0 + eslint-plugin-prettier: ^5.2.3 + eslint-plugin-regexp: ^2.7.0 + eslint-plugin-unicorn: ^56.0.1 + eslint-plugin-unused-imports: ^4.1.4 + eslint-plugin-vitest: ^0.5.4 + eslint-plugin-vue: ^9.32.0 + execa: ^9.5.2 + find-up: ^7.0.0 + get-port: ^7.1.0 + globals: ^15.14.0 + h3: ^1.14.0 + happy-dom: ^16.8.1 + html-minifier-terser: ^7.2.0 + husky: ^9.1.7 + is-ci: ^4.1.0 + jsonc-eslint-parser: ^2.4.0 + jsonwebtoken: ^9.0.2 + lint-staged: ^15.4.3 + lodash.clonedeep: ^4.5.0 + lodash.get: ^4.4.2 + lodash.isequal: ^4.5.0 + lucide-vue-next: ^0.469.0 + medium-zoom: ^1.1.0 + naive-ui: ^2.41.0 + nitropack: ^2.10.4 + nprogress: ^0.2.0 + ora: ^8.2.0 + pinia: ^2.3.1 + pinia-plugin-persistedstate: ^4.2.0 + pkg-types: ^1.3.1 + playwright: ^1.50.1 + postcss: ^8.5.1 + postcss-antd-fixes: ^0.2.0 + postcss-html: ^1.8.0 + postcss-import: ^16.1.0 + postcss-preset-env: ^10.1.3 + postcss-scss: ^4.0.9 + prettier: ^3.4.2 + prettier-plugin-tailwindcss: ^0.6.11 + publint: ^0.2.12 + qrcode: ^1.5.4 + radix-vue: ^1.9.13 + resolve.exports: ^2.0.3 + rimraf: ^6.0.1 + rollup: ^4.34.2 + rollup-plugin-visualizer: ^5.14.0 + sass: ^1.83.4 + sortablejs: ^1.15.6 + stylelint: ^16.14.1 + stylelint-config-recess-order: ^5.1.1 + stylelint-config-recommended: ^14.0.1 + stylelint-config-recommended-scss: ^14.1.0 + stylelint-config-recommended-vue: ^1.6.0 + stylelint-config-standard: ^36.0.1 + stylelint-order: ^6.0.4 + stylelint-prettier: ^5.0.3 + stylelint-scss: ^6.11.0 + tailwind-merge: ^2.6.0 + tailwindcss: ^3.4.17 + tailwindcss-animate: ^1.0.7 + theme-colors: ^0.1.0 + tippy.js: ^6.2.5 + turbo: ^2.4.0 + typescript: ^5.7.3 + unbuild: ^3.3.1 + unplugin-element-plus: ^0.9.0 + vee-validate: ^4.15.0 + vite: ^6.0.11 + vite-plugin-compression: ^0.5.1 + vite-plugin-dts: ^4.5.0 + vite-plugin-html: ^3.2.2 + vite-plugin-lazy-import: ^1.0.7 + vite-plugin-pwa: ^0.21.1 + vite-plugin-vue-devtools: ^7.7.1 + vitepress: ^1.6.3 + vitepress-plugin-group-icons: ^1.3.5 + vitest: ^2.1.9 + vue: ^3.5.13 + vue-eslint-parser: ^9.4.3 + vue-i18n: ^11.1.0 + vue-router: ^4.5.0 + vue-tippy: ^6.6.0 + vue-tsc: 2.1.10 + vxe-pc-ui: ^4.3.79 + vxe-table: 4.10.0 + watermark-js-plus: ^1.5.7 + zod: ^3.24.1 + zod-defaults: ^0.1.3 diff --git a/scripts/clean.mjs b/scripts/clean.mjs new file mode 100644 index 0000000..e2840d3 --- /dev/null +++ b/scripts/clean.mjs @@ -0,0 +1,53 @@ +import { promises as fs } from 'node:fs'; +import { join } from 'node:path'; + +const rootDir = process.cwd(); + +/** + * 递归查找并删除目标目录 + * @param {string} currentDir - 当前遍历的目录路径 + */ +async function cleanTargetsRecursively(currentDir, targets) { + const items = await fs.readdir(currentDir); + + for (const item of items) { + try { + const itemPath = join(currentDir, item); + if (targets.includes(item)) { + // 匹配到目标目录或文件时直接删除 + await fs.rm(itemPath, { force: true, recursive: true }); + console.log(`Deleted: ${itemPath}`); + } + const stat = await fs.lstat(itemPath); + if (stat.isDirectory()) { + await cleanTargetsRecursively(itemPath, targets); + } + } catch { + // console.error( + // `Error handling item ${item} in ${currentDir}: ${error.message}`, + // ); + } + } +} + +(async function startCleanup() { + // 要删除的目录及文件名称 + const targets = ['node_modules', 'dist', '.turbo', 'dist.zip']; + + const deleteLockFile = process.argv.includes('--del-lock'); + const cleanupTargets = [...targets]; + if (deleteLockFile) { + cleanupTargets.push('pnpm-lock.yaml'); + } + + console.log( + `Starting cleanup of targets: ${cleanupTargets.join(', ')} from root: ${rootDir}`, + ); + + try { + await cleanTargetsRecursively(rootDir, cleanupTargets); + console.log('Cleanup process completed.'); + } catch (error) { + console.error(`Unexpected error during cleanup: ${error.message}`); + } +})(); diff --git a/scripts/deploy/Dockerfile b/scripts/deploy/Dockerfile new file mode 100644 index 0000000..9fd1519 --- /dev/null +++ b/scripts/deploy/Dockerfile @@ -0,0 +1,31 @@ +FROM node:20-slim AS builder + +# --max-old-space-size +ENV PNPM_HOME="/pnpm" +ENV PATH="$PNPM_HOME:$PATH" +ENV NODE_OPTIONS=--max-old-space-size=8192 +ENV TZ=Asia/Shanghai + +RUN corepack enable + +WORKDIR /app + +# copy package.json and pnpm-lock.yaml to workspace +COPY . /app + +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile +RUN pnpm run build --filter=\!./docs + +RUN echo "Builder Success 🎉" + +FROM nginx:stable-alpine AS production + +RUN echo "types { application/javascript js mjs; }" > /etc/nginx/conf.d/mjs.conf +COPY --from=builder /app/playground/dist /usr/share/nginx/html + +COPY --from=builder /app/scripts/deploy/nginx.conf /etc/nginx/nginx.conf + +EXPOSE 8080 + +# start nginx +CMD ["nginx", "-g", "daemon off;"] diff --git a/scripts/deploy/build-local-docker-image.sh b/scripts/deploy/build-local-docker-image.sh new file mode 100644 index 0000000..4881487 --- /dev/null +++ b/scripts/deploy/build-local-docker-image.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +LOG_FILE=${SCRIPT_DIR}/build-local-docker-image.log +ERROR="" +IMAGE_NAME="vben-admin-local" + +function stop_and_remove_container() { + # Stop and remove the existing container + docker stop ${IMAGE_NAME} >/dev/null 2>&1 + docker rm ${IMAGE_NAME} >/dev/null 2>&1 +} + +function remove_image() { + # Remove the existing image + docker rmi vben-admin-pro >/dev/null 2>&1 +} + +function install_dependencies() { + # Install all dependencies + cd ${SCRIPT_DIR} + pnpm install || ERROR="install_dependencies failed" +} + +function build_image() { + # build docker + docker build ../../ -f Dockerfile -t ${IMAGE_NAME} || ERROR="build_image failed" +} + +function log_message() { + if [[ ${ERROR} != "" ]]; + then + >&2 echo "build failed, Please check build-local-docker-image.log for more details" + >&2 echo "ERROR: ${ERROR}" + exit 1 + else + echo "docker image with tag '${IMAGE_NAME}' built sussessfully. Use below sample command to run the container" + echo "" + echo "docker run -d -p 8010:8080 --name ${IMAGE_NAME} ${IMAGE_NAME}" + fi +} + +echo "Info: Stopping and removing existing container and image" | tee ${LOG_FILE} +stop_and_remove_container +remove_image + +echo "Info: Installing dependencies" | tee -a ${LOG_FILE} +install_dependencies 1>> ${LOG_FILE} 2>> ${LOG_FILE} + +if [[ ${ERROR} == "" ]]; then + echo "Info: Building docker image" | tee -a ${LOG_FILE} + build_image 1>> ${LOG_FILE} 2>> ${LOG_FILE} +fi + +log_message | tee -a ${LOG_FILE} diff --git a/scripts/deploy/nginx.conf b/scripts/deploy/nginx.conf new file mode 100644 index 0000000..8e6ab10 --- /dev/null +++ b/scripts/deploy/nginx.conf @@ -0,0 +1,75 @@ + +#user nobody; +worker_processes 1; + +#error_log logs/error.log; +#error_log logs/error.log notice; +#error_log logs/error.log info; + +#pid logs/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include mime.types; + default_type application/octet-stream; + + types { + application/javascript js mjs; + text/css css; + text/html html; + } + + sendfile on; + # tcp_nopush on; + + #keepalive_timeout 0; + # keepalive_timeout 65; + + # gzip on; + # gzip_buffers 32 16k; + # gzip_comp_level 6; + # gzip_min_length 1k; + # gzip_static on; + # gzip_types text/plain + # text/css + # application/javascript + # application/json + # application/x-javascript + # text/xml + # application/xml + # application/xml+rss + # text/javascript; #设置压缩的文件类型 + # gzip_vary on; + + server { + listen 8080; + server_name localhost; + + location / { + root /usr/share/nginx/html; + try_files $uri $uri/ /index.html; + index index.html; + # Enable CORS + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; + add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; + if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Max-Age' 1728000; + add_header 'Content-Type' 'text/plain charset=UTF-8'; + add_header 'Content-Length' 0; + return 204; + } + } + + error_page 500 502 503 504 /50x.html; + + location = /50x.html { + root /usr/share/nginx/html; + } + } +} diff --git a/scripts/turbo-run/README.md b/scripts/turbo-run/README.md new file mode 100644 index 0000000..65ffcf4 --- /dev/null +++ b/scripts/turbo-run/README.md @@ -0,0 +1,3 @@ +# @vben/turbo-run + +turbo-run is a command line tool that allows you to run multiple commands in parallel. diff --git a/scripts/turbo-run/bin/turbo-run.mjs b/scripts/turbo-run/bin/turbo-run.mjs new file mode 100644 index 0000000..407754d --- /dev/null +++ b/scripts/turbo-run/bin/turbo-run.mjs @@ -0,0 +1,3 @@ +#!/usr/bin/env node + +import('../dist/index.mjs'); diff --git a/scripts/turbo-run/build.config.ts b/scripts/turbo-run/build.config.ts new file mode 100644 index 0000000..97e572c --- /dev/null +++ b/scripts/turbo-run/build.config.ts @@ -0,0 +1,7 @@ +import { defineBuildConfig } from 'unbuild'; + +export default defineBuildConfig({ + clean: true, + declaration: true, + entries: ['src/index'], +}); diff --git a/scripts/turbo-run/package.json b/scripts/turbo-run/package.json new file mode 100644 index 0000000..8128ec5 --- /dev/null +++ b/scripts/turbo-run/package.json @@ -0,0 +1,29 @@ +{ + "name": "@vben/turbo-run", + "version": "5.5.3", + "private": true, + "license": "MIT", + "type": "module", + "scripts": { + "stub": "pnpm unbuild --stub" + }, + "files": [ + "dist" + ], + "bin": { + "turbo-run": "./bin/turbo-run.mjs" + }, + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "exports": { + ".": { + "default": "./dist/index.mjs" + }, + "./package.json": "./package.json" + }, + "dependencies": { + "@clack/prompts": "catalog:", + "@vben/node-utils": "workspace:*", + "cac": "catalog:" + } +} diff --git a/scripts/turbo-run/src/index.ts b/scripts/turbo-run/src/index.ts new file mode 100644 index 0000000..2f8dad0 --- /dev/null +++ b/scripts/turbo-run/src/index.ts @@ -0,0 +1,29 @@ +import { colors, consola } from '@vben/node-utils'; + +import { cac } from 'cac'; + +import { run } from './run'; + +try { + const turboRun = cac('turbo-run'); + + turboRun + .command('[script]') + .usage(`Run turbo interactively.`) + .action(async (command: string) => { + run({ command }); + }); + + // Invalid command + turboRun.on('command:*', () => { + consola.error(colors.red('Invalid command!')); + process.exit(1); + }); + + turboRun.usage('turbo-run'); + turboRun.help(); + turboRun.parse(); +} catch (error) { + consola.error(error); + process.exit(1); +} diff --git a/scripts/turbo-run/src/run.ts b/scripts/turbo-run/src/run.ts new file mode 100644 index 0000000..6a8762f --- /dev/null +++ b/scripts/turbo-run/src/run.ts @@ -0,0 +1,67 @@ +import { execaCommand, getPackages } from '@vben/node-utils'; + +import { cancel, isCancel, select } from '@clack/prompts'; + +interface RunOptions { + command?: string; +} + +export async function run(options: RunOptions) { + const { command } = options; + if (!command) { + console.error('Please enter the command to run'); + process.exit(1); + } + const { packages } = await getPackages(); + // const appPkgs = await findApps(process.cwd(), packages); + // const websitePkg = packages.find( + // (item) => item.packageJson.name === '@vben/website', + // ); + + // 只显示有对应命令的包 + const selectPkgs = packages.filter((pkg) => { + return (pkg?.packageJson as Record)?.scripts?.[command]; + }); + + let selectPkg: string | symbol; + if (selectPkgs.length > 1) { + selectPkg = await select({ + message: `Select the app you need to run [${command}]:`, + options: selectPkgs.map((item) => ({ + label: item?.packageJson.name, + value: item?.packageJson.name, + })), + }); + + if (isCancel(selectPkg) || !selectPkg) { + cancel('👋 Has cancelled'); + process.exit(0); + } + } else { + selectPkg = selectPkgs[0]?.packageJson?.name ?? ''; + } + + if (!selectPkg) { + console.error('No app found'); + process.exit(1); + } + + execaCommand(`pnpm --filter=${selectPkg} run ${command}`, { + stdio: 'inherit', + }); +} + +/** + * 过滤app包 + * @param root + * @param packages + */ +// async function findApps(root: string, packages: Package[]) { +// // apps内的 +// const appPackages = packages.filter((pkg) => { +// const viteConfigExists = fs.existsSync(join(pkg.dir, 'vite.config.mts')); +// return pkg.dir.startsWith(join(root, 'apps')) && viteConfigExists; +// }); + +// return appPackages; +// } diff --git a/scripts/turbo-run/tsconfig.json b/scripts/turbo-run/tsconfig.json new file mode 100644 index 0000000..b2ec3b6 --- /dev/null +++ b/scripts/turbo-run/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/node.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/scripts/vsh/README.md b/scripts/vsh/README.md new file mode 100644 index 0000000..7cc8fbf --- /dev/null +++ b/scripts/vsh/README.md @@ -0,0 +1,3 @@ +# @vben/vsh + +shell 脚本工具集合 diff --git a/scripts/vsh/bin/vsh.mjs b/scripts/vsh/bin/vsh.mjs new file mode 100644 index 0000000..407754d --- /dev/null +++ b/scripts/vsh/bin/vsh.mjs @@ -0,0 +1,3 @@ +#!/usr/bin/env node + +import('../dist/index.mjs'); diff --git a/scripts/vsh/build.config.ts b/scripts/vsh/build.config.ts new file mode 100644 index 0000000..97e572c --- /dev/null +++ b/scripts/vsh/build.config.ts @@ -0,0 +1,7 @@ +import { defineBuildConfig } from 'unbuild'; + +export default defineBuildConfig({ + clean: true, + declaration: true, + entries: ['src/index'], +}); diff --git a/scripts/vsh/package.json b/scripts/vsh/package.json new file mode 100644 index 0000000..9767a4d --- /dev/null +++ b/scripts/vsh/package.json @@ -0,0 +1,31 @@ +{ + "name": "@vben/vsh", + "version": "5.5.3", + "private": true, + "license": "MIT", + "type": "module", + "scripts": { + "stub": "pnpm unbuild --stub" + }, + "files": [ + "dist" + ], + "bin": { + "vsh": "./bin/vsh.mjs" + }, + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "exports": { + ".": { + "default": "./dist/index.mjs" + }, + "./package.json": "./package.json" + }, + "dependencies": { + "@vben/node-utils": "workspace:*", + "cac": "catalog:", + "circular-dependency-scanner": "catalog:", + "depcheck": "catalog:", + "publint": "catalog:" + } +} diff --git a/scripts/vsh/src/check-circular/index.ts b/scripts/vsh/src/check-circular/index.ts new file mode 100644 index 0000000..8af9789 --- /dev/null +++ b/scripts/vsh/src/check-circular/index.ts @@ -0,0 +1,80 @@ +import type { CAC } from 'cac'; + +import { extname } from 'node:path'; + +import { getStagedFiles } from '@vben/node-utils'; + +import { circularDepsDetect, printCircles } from 'circular-dependency-scanner'; + +const IGNORE_DIR = [ + 'dist', + '.turbo', + 'output', + '.cache', + 'scripts', + 'internal', + 'packages/effects/request/src/', + 'packages/@core/ui-kit/menu-ui/src/', + 'packages/@core/ui-kit/popup-ui/src/', +].join(','); + +const IGNORE = [`**/{${IGNORE_DIR}}/**`]; + +interface CommandOptions { + staged: boolean; + verbose: boolean; +} + +async function checkCircular({ staged, verbose }: CommandOptions) { + const results = await circularDepsDetect({ + absolute: staged, + cwd: process.cwd(), + ignore: IGNORE, + }); + + if (staged) { + let files = await getStagedFiles(); + + const allowedExtensions = new Set([ + '.cjs', + '.js', + '.jsx', + '.mjs', + '.ts', + '.tsx', + '.vue', + ]); + + // 过滤文件列表 + files = files.filter((file) => allowedExtensions.has(extname(file))); + + const circularFiles: string[][] = []; + + for (const file of files) { + for (const result of results) { + const resultFiles = result.flat(); + if (resultFiles.includes(file)) { + circularFiles.push(result); + } + } + } + verbose && printCircles(circularFiles); + } else { + verbose && printCircles(results); + } +} + +function defineCheckCircularCommand(cac: CAC) { + cac + .command('check-circular') + .option( + '--staged', + 'Whether it is the staged commit mode, in which mode, if there is a circular dependency, an alarm will be given.', + ) + .usage(`Analysis of project circular dependencies.`) + .action(async ({ staged }) => { + await checkCircular({ staged, verbose: true }); + }); +} + +export { defineCheckCircularCommand }; diff --git a/scripts/vsh/src/check-dep/index.ts b/scripts/vsh/src/check-dep/index.ts new file mode 100644 index 0000000..7626229 --- /dev/null +++ b/scripts/vsh/src/check-dep/index.ts @@ -0,0 +1,85 @@ +import type { CAC } from 'cac'; + +import { getPackages } from '@vben/node-utils'; + +import depcheck from 'depcheck'; + +async function runDepcheck() { + const { packages } = await getPackages(); + await Promise.all( + packages.map(async (pkg) => { + if ( + [ + '@vben/backend-mock', + '@vben/commitlint-config', + '@vben/eslint-config', + '@vben/lint-staged-config', + '@vben/node-utils', + '@vben/prettier-config', + '@vben/stylelint-config', + '@vben/tailwind-config', + '@vben/tsconfig', + '@vben/vite-config', + '@vben/vite-config', + '@vben/vsh', + ].includes(pkg.packageJson.name) + ) { + return; + } + + const unused = await depcheck(pkg.dir, { + ignoreMatches: [ + 'vite', + 'vitest', + 'unbuild', + '@vben/tsconfig', + '@vben/vite-config', + '@vben/tailwind-config', + '@types/*', + '@vben-core/design', + ], + ignorePatterns: ['dist', 'node_modules', 'public'], + }); + + // 删除file:前缀的依赖提示,该依赖是本地依赖 + Reflect.deleteProperty(unused.missing, 'file:'); + Object.keys(unused.missing).forEach((key) => { + unused.missing[key] = (unused.missing[key] || []).filter( + (item: string) => !item.startsWith('/'), + ); + if (unused.missing[key].length === 0) { + Reflect.deleteProperty(unused.missing, key); + } + }); + + if ( + Object.keys(unused.missing).length === 0 && + unused.dependencies.length === 0 && + unused.devDependencies.length === 0 + ) { + return; + } + console.error( + '\n', + pkg.packageJson.name, + '\n missing:', + unused.missing, + '\n dependencies:', + unused.dependencies, + '\n devDependencies:', + unused.devDependencies, + ); + }), + ); +} + +function defineDepcheckCommand(cac: CAC) { + cac + .command('check-dep') + .usage(`Analysis of project circular dependencies.`) + .action(async () => { + await runDepcheck(); + }); +} + +export { defineDepcheckCommand }; diff --git a/scripts/vsh/src/code-workspace/index.ts b/scripts/vsh/src/code-workspace/index.ts new file mode 100644 index 0000000..d5ec4ee --- /dev/null +++ b/scripts/vsh/src/code-workspace/index.ts @@ -0,0 +1,78 @@ +import type { CAC } from 'cac'; + +import { join, relative } from 'node:path'; + +import { + colors, + consola, + findMonorepoRoot, + getPackages, + gitAdd, + outputJSON, + prettierFormat, + toPosixPath, +} from '@vben/node-utils'; + +const CODE_WORKSPACE_FILE = join('vben-admin.code-workspace'); + +interface CodeWorkspaceCommandOptions { + autoCommit?: boolean; + spaces?: number; +} + +async function createCodeWorkspace({ + autoCommit = false, + spaces = 2, +}: CodeWorkspaceCommandOptions) { + const { packages, rootDir } = await getPackages(); + + let folders = packages.map((pkg) => { + const { dir, packageJson } = pkg; + return { + name: packageJson.name, + path: toPosixPath(relative(rootDir, dir)), + }; + }); + + folders = folders.filter(Boolean); + + const monorepoRoot = findMonorepoRoot(); + const outputPath = join(monorepoRoot, CODE_WORKSPACE_FILE); + await outputJSON(outputPath, { folders }, spaces); + + await prettierFormat(outputPath); + if (autoCommit) { + await gitAdd(CODE_WORKSPACE_FILE, monorepoRoot); + } +} + +async function runCodeWorkspace({ + autoCommit, + spaces, +}: CodeWorkspaceCommandOptions) { + await createCodeWorkspace({ + autoCommit, + spaces, + }); + if (autoCommit) { + return; + } + consola.log(''); + consola.success(colors.green(`${CODE_WORKSPACE_FILE} is updated!`)); + consola.log(''); +} + +function defineCodeWorkspaceCommand(cac: CAC) { + cac + .command('code-workspace') + .usage('Update the `.code-workspace` file') + .option('--spaces [number]', '.code-workspace JSON file spaces.', { + default: 2, + }) + .option('--auto-commit', 'auto commit .code-workspace JSON file.', { + default: false, + }) + .action(runCodeWorkspace); +} + +export { defineCodeWorkspaceCommand }; diff --git a/scripts/vsh/src/index.ts b/scripts/vsh/src/index.ts new file mode 100644 index 0000000..2b00448 --- /dev/null +++ b/scripts/vsh/src/index.ts @@ -0,0 +1,41 @@ +import { colors, consola } from '@vben/node-utils'; + +import { cac } from 'cac'; + +import { defineCheckCircularCommand } from './check-circular'; +import { defineDepcheckCommand } from './check-dep'; +import { defineCodeWorkspaceCommand } from './code-workspace'; +import { defineLintCommand } from './lint'; +import { definePubLintCommand } from './publint'; + +try { + const vsh = cac('vsh'); + + // vsh lint + defineLintCommand(vsh); + + // vsh publint + definePubLintCommand(vsh); + + // vsh code-workspace + defineCodeWorkspaceCommand(vsh); + + // vsh check-circular + defineCheckCircularCommand(vsh); + + // vsh check-dep + defineDepcheckCommand(vsh); + + // Invalid command + vsh.on('command:*', () => { + consola.error(colors.red('Invalid command!')); + process.exit(1); + }); + + vsh.usage('vsh'); + vsh.help(); + vsh.parse(); +} catch (error) { + consola.error(error); + process.exit(1); +} diff --git a/scripts/vsh/src/lint/index.ts b/scripts/vsh/src/lint/index.ts new file mode 100644 index 0000000..5d381e8 --- /dev/null +++ b/scripts/vsh/src/lint/index.ts @@ -0,0 +1,48 @@ +import type { CAC } from 'cac'; + +import { execaCommand } from '@vben/node-utils'; + +interface LintCommandOptions { + /** + * Format lint problem. + */ + format?: boolean; +} + +async function runLint({ format }: LintCommandOptions) { + // process.env.FORCE_COLOR = '3'; + + if (format) { + await execaCommand(`stylelint "**/*.{vue,css,less.scss}" --cache --fix`, { + stdio: 'inherit', + }); + await execaCommand(`eslint . --cache --fix`, { + stdio: 'inherit', + }); + await execaCommand(`prettier . --write --cache --log-level warn`, { + stdio: 'inherit', + }); + return; + } + await Promise.all([ + execaCommand(`eslint . --cache`, { + stdio: 'inherit', + }), + execaCommand(`prettier . --ignore-unknown --check --cache`, { + stdio: 'inherit', + }), + execaCommand(`stylelint "**/*.{vue,css,less.scss}" --cache`, { + stdio: 'inherit', + }), + ]); +} + +function defineLintCommand(cac: CAC) { + cac + .command('lint') + .usage('Batch execute project lint check.') + .option('--format', 'Format lint problem.') + .action(runLint); +} + +export { defineLintCommand }; diff --git a/scripts/vsh/src/publint/index.ts b/scripts/vsh/src/publint/index.ts new file mode 100644 index 0000000..622547a --- /dev/null +++ b/scripts/vsh/src/publint/index.ts @@ -0,0 +1,184 @@ +import type { CAC } from 'cac'; +import type { Result } from 'publint'; + +import { basename, dirname, join } from 'node:path'; + +import { + colors, + consola, + ensureFile, + findMonorepoRoot, + generatorContentHash, + getPackages, + outputJSON, + readJSON, + UNICODE, +} from '@vben/node-utils'; +import { publint } from 'publint'; +import { formatMessage } from 'publint/utils'; + +const CACHE_FILE = join( + 'node_modules', + '.cache', + 'publint', + '.pkglintcache.json', +); + +interface PubLintCommandOptions { + /** + * Only errors are checked, no program exit is performed + */ + check?: boolean; +} + +/** + * Get files that require lint + * @param files + */ +async function getLintFiles(files: string[] = []) { + const lintFiles: string[] = []; + + if (files?.length > 0) { + return files.filter((file) => basename(file) === 'package.json'); + } + + const { packages } = await getPackages(); + + for (const { dir } of packages) { + lintFiles.push(join(dir, 'package.json')); + } + return lintFiles; +} + +function getCacheFile() { + const root = findMonorepoRoot(); + return join(root, CACHE_FILE); +} + +async function readCache(cacheFile: string) { + try { + await ensureFile(cacheFile); + return await readJSON(cacheFile); + } catch { + return {}; + } +} + +async function runPublint(files: string[], { check }: PubLintCommandOptions) { + const lintFiles = await getLintFiles(files); + const cacheFile = getCacheFile(); + + const cacheData = await readCache(cacheFile); + const cache: Record = cacheData; + + const results = await Promise.all( + lintFiles.map(async (file) => { + try { + const pkgJson = await readJSON(file); + + if (pkgJson.private) { + return null; + } + + Reflect.deleteProperty(pkgJson, 'dependencies'); + Reflect.deleteProperty(pkgJson, 'devDependencies'); + Reflect.deleteProperty(pkgJson, 'peerDependencies'); + const content = JSON.stringify(pkgJson); + const hash = generatorContentHash(content); + + const publintResult: Result = + cache?.[file]?.hash === hash + ? (cache?.[file]?.result ?? []) + : await publint({ + level: 'suggestion', + pkgDir: dirname(file), + strict: true, + }); + + cache[file] = { + hash, + result: publintResult, + }; + + return { pkgJson, pkgPath: file, publintResult }; + } catch { + return null; + } + }), + ); + + await outputJSON(cacheFile, cache); + printResult(results, check); +} + +function printResult( + results: Array<{ + pkgJson: Record; + pkgPath: string; + publintResult: Result; + } | null>, + check?: boolean, +) { + let errorCount = 0; + let warningCount = 0; + let suggestionsCount = 0; + + for (const result of results) { + if (!result) { + continue; + } + const { pkgJson, pkgPath, publintResult } = result; + const messages = publintResult?.messages ?? []; + if (messages?.length < 1) { + continue; + } + + consola.log(''); + consola.log(pkgPath); + for (const message of messages) { + switch (message.type) { + case 'error': { + errorCount++; + + break; + } + case 'suggestion': { + suggestionsCount++; + break; + } + case 'warning': { + warningCount++; + + break; + } + // No default + } + const ruleUrl = `https://publint.dev/rules#${message.code.toLocaleLowerCase()}`; + consola.log( + ` ${formatMessage(message, pkgJson)}${colors.dim(` ${ruleUrl}`)}`, + ); + } + } + + const totalCount = warningCount + errorCount + suggestionsCount; + if (totalCount > 0) { + consola.error( + colors.red( + `${UNICODE.FAILURE} ${totalCount} problem (${errorCount} errors, ${warningCount} warnings, ${suggestionsCount} suggestions)`, + ), + ); + !check && process.exit(1); + } else { + consola.log(colors.green(`${UNICODE.SUCCESS} No problem`)); + } +} + +function definePubLintCommand(cac: CAC) { + cac + .command('publint [...files]') + .usage('Check if the monorepo package conforms to the publint standard.') + .option('--check', 'Only errors are checked, no program exit is performed.') + .action(runPublint); +} + +export { definePubLintCommand }; diff --git a/scripts/vsh/tsconfig.json b/scripts/vsh/tsconfig.json new file mode 100644 index 0000000..b2ec3b6 --- /dev/null +++ b/scripts/vsh/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/node.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/scripts/代码生成模板/VelocityUtils.java b/scripts/代码生成模板/VelocityUtils.java new file mode 100644 index 0000000..795ce58 --- /dev/null +++ b/scripts/代码生成模板/VelocityUtils.java @@ -0,0 +1,410 @@ +package org.dromara.generator.util; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.ObjectUtil; +import org.dromara.generator.constant.GenConstants; +import org.dromara.common.core.utils.DateUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.common.mybatis.helper.DataBaseHelper; +import org.dromara.generator.domain.GenTable; +import org.dromara.generator.domain.GenTableColumn; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.apache.velocity.VelocityContext; + +import java.util.*; + +/** + * 模板处理工具类 + * + * @author ruoyi + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class VelocityUtils { + + /** + * 项目空间路径 + */ + private static final String PROJECT_PATH = "main/java"; + + /** + * mybatis空间路径 + */ + private static final String MYBATIS_PATH = "main/resources/mapper"; + + /** + * 默认上级菜单,系统工具 + */ + private static final String DEFAULT_PARENT_MENU_ID = "3"; + + /** + * 设置模板变量信息 + * + * @return 模板列表 + */ + public static VelocityContext prepareContext(GenTable genTable) { + String moduleName = genTable.getModuleName(); + String businessName = genTable.getBusinessName(); + String packageName = genTable.getPackageName(); + String tplCategory = genTable.getTplCategory(); + String functionName = genTable.getFunctionName(); + + VelocityContext velocityContext = new VelocityContext(); + velocityContext.put("tplCategory", genTable.getTplCategory()); + velocityContext.put("tableName", genTable.getTableName()); + velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】"); + velocityContext.put("ClassName", genTable.getClassName()); + velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName())); + velocityContext.put("moduleName", genTable.getModuleName()); + velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName())); + velocityContext.put("businessName", genTable.getBusinessName()); + velocityContext.put("basePackage", getPackagePrefix(packageName)); + velocityContext.put("packageName", packageName); + velocityContext.put("author", genTable.getFunctionAuthor()); + velocityContext.put("datetime", DateUtils.getDate()); + velocityContext.put("pkColumn", genTable.getPkColumn()); + velocityContext.put("importList", getImportList(genTable)); + velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName)); + velocityContext.put("columns", genTable.getColumns()); + velocityContext.put("table", genTable); + velocityContext.put("dicts", getDicts(genTable)); + setMenuVelocityContext(velocityContext, genTable); + if (GenConstants.TPL_TREE.equals(tplCategory)) { + setTreeVelocityContext(velocityContext, genTable); + } + // 判断是modal还是drawer + Dict paramsObj = JsonUtils.parseMap(genTable.getOptions()); + if (ObjectUtil.isNotNull(paramsObj)) { + String popupComponent = Optional + .ofNullable(paramsObj.getStr("popupComponent")) + .orElse("modal"); + velocityContext.put("popupComponent", popupComponent); + velocityContext.put("PopupComponent", StringUtils.capitalize(popupComponent)); + } else { + velocityContext.put("popupComponent", "modal"); + velocityContext.put("PopupComponent", "Modal"); + } + // 判断是原生antd表单还是useForm表单 + // native 原生antd表单 + // useForm useVbenForm + if (ObjectUtil.isNotNull(paramsObj)) { + String formComponent = Optional + .ofNullable(paramsObj.getStr("formComponent")) + .orElse("useForm"); + velocityContext.put("formComponent", formComponent); + } + return velocityContext; + } + + public static void setMenuVelocityContext(VelocityContext context, GenTable genTable) { + String options = genTable.getOptions(); + Dict paramsObj = JsonUtils.parseMap(options); + String parentMenuId = getParentMenuId(paramsObj); + context.put("parentMenuId", parentMenuId); + } + + public static void setTreeVelocityContext(VelocityContext context, GenTable genTable) { + String options = genTable.getOptions(); + Dict paramsObj = JsonUtils.parseMap(options); + String treeCode = getTreecode(paramsObj); + String treeParentCode = getTreeParentCode(paramsObj); + String treeName = getTreeName(paramsObj); + + context.put("treeCode", treeCode); + context.put("treeParentCode", treeParentCode); + context.put("treeName", treeName); + context.put("expandColumn", getExpandColumn(genTable)); + if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) { + context.put("tree_parent_code", paramsObj.get(GenConstants.TREE_PARENT_CODE)); + } + if (paramsObj.containsKey(GenConstants.TREE_NAME)) { + context.put("tree_name", paramsObj.get(GenConstants.TREE_NAME)); + } + } + + /** + * 获取模板信息 + * + * @return 模板列表 + */ + public static List getTemplateList(String tplCategory) { + List templates = new ArrayList<>(); + templates.add("vm/java/domain.java.vm"); + templates.add("vm/java/vo.java.vm"); + templates.add("vm/java/bo.java.vm"); + templates.add("vm/java/mapper.java.vm"); + templates.add("vm/java/service.java.vm"); + templates.add("vm/java/serviceImpl.java.vm"); + templates.add("vm/java/controller.java.vm"); + templates.add("vm/xml/mapper.xml.vm"); + if (DataBaseHelper.isOracle()) { + templates.add("vm/sql/oracle/sql.vm"); + } else if (DataBaseHelper.isPostgerSql()) { + templates.add("vm/sql/postgres/sql.vm"); + } else if (DataBaseHelper.isSqlServer()) { + templates.add("vm/sql/sqlserver/sql.vm"); + } else { + templates.add("vm/sql/sql.vm"); + } + templates.add("vm/ts/api.ts.vm"); + templates.add("vm/ts/types.ts.vm"); + if (GenConstants.TPL_CRUD.equals(tplCategory)) { + templates.add("vm/vue/index.vue.vm"); + } else if (GenConstants.TPL_TREE.equals(tplCategory)) { + templates.add("vm/vue/index-tree.vue.vm"); + } + + /** + * 添加vben5 + */ + templates.add("vm/vben5/api/index.ts.vm"); + templates.add("vm/vben5/api/model.d.ts.vm"); + templates.add("vm/vben5/views/data.ts.vm"); + if (GenConstants.TPL_CRUD.equals(tplCategory)) { + templates.add("vm/vben5/views/index_vben.vue.vm"); + templates.add("vm/vben5/views/popup.vue.vm"); + } else if (GenConstants.TPL_TREE.equals(tplCategory)) { + templates.add("vm/vben5/views/index_vben_tree.vue.vm"); + templates.add("vm/vben5/views/popup_tree.vue.vm"); + } + + return templates; + } + + /** + * 获取文件名 + */ + public static String getFileName(String template, GenTable genTable) { + // 文件名称 + String fileName = ""; + // 包路径 + String packageName = genTable.getPackageName(); + // 模块名 + String moduleName = genTable.getModuleName(); + // 大写类名 + String className = genTable.getClassName(); + // 业务名称 + String businessName = genTable.getBusinessName(); + + String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/"); + String mybatisPath = MYBATIS_PATH + "/" + moduleName; + String vuePath = "vue"; + + if (template.contains("domain.java.vm")) { + fileName = StringUtils.format("{}/domain/{}.java", javaPath, className); + } + if (template.contains("vo.java.vm")) { + fileName = StringUtils.format("{}/domain/vo/{}Vo.java", javaPath, className); + } + if (template.contains("bo.java.vm")) { + fileName = StringUtils.format("{}/domain/bo/{}Bo.java", javaPath, className); + } + if (template.contains("mapper.java.vm")) { + fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className); + } else if (template.contains("service.java.vm")) { + fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className); + } else if (template.contains("serviceImpl.java.vm")) { + fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className); + } else if (template.contains("controller.java.vm")) { + fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className); + } else if (template.contains("mapper.xml.vm")) { + fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className); + } else if (template.contains("sql.vm")) { + fileName = businessName + "Menu.sql"; + } else if (template.contains("api.ts.vm")) { + fileName = StringUtils.format("{}/api/{}/{}/index.ts", vuePath, moduleName, businessName); + } else if (template.contains("types.ts.vm")) { + fileName = StringUtils.format("{}/api/{}/{}/types.ts", vuePath, moduleName, businessName); + } else if (template.contains("index.vue.vm")) { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } else if (template.contains("index-tree.vue.vm")) { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } + + // 判断是modal还是drawer + Dict paramsObj = JsonUtils.parseMap(genTable.getOptions()); + String popupComponent = "modal"; + if (ObjectUtil.isNotNull(paramsObj)) { + popupComponent = Optional + .ofNullable(paramsObj.getStr("popupComponent")) + .orElse("modal"); + } + String vben5Path = "vben5"; + if (template.contains("vm/vben5/api/index.ts.vm")) { + fileName = StringUtils.format("{}/api/{}/{}/index.ts", vben5Path, moduleName, businessName); + } + if (template.contains("vm/vben5/api/model.d.ts.vm")) { + fileName = StringUtils.format("{}/api/{}/{}/model.d.ts", vben5Path, moduleName, businessName); + } + if (template.contains("vm/vben5/views/index_vben.vue.vm")) { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vben5Path, moduleName, businessName); + } + if (template.contains("vm/vben5/views/index_vben_tree.vue.vm")) { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vben5Path, moduleName, businessName); + } + if (template.contains("vm/vben5/views/data.ts.vm")) { + fileName = StringUtils.format("{}/views/{}/{}/data.ts", vben5Path, moduleName, businessName); + } + if (template.contains("vm/vben5/views/popup.vue.vm")) { + fileName = StringUtils.format("{}/views/{}/{}/{}-{}.vue", vben5Path, moduleName, businessName, businessName, popupComponent); + } + if (template.contains("vm/vben5/views/popup_tree.vue.vm")) { + fileName = StringUtils.format("{}/views/{}/{}/{}-{}.vue", vben5Path, moduleName, businessName, businessName, popupComponent); + } + + return fileName; + } + + /** + * 获取包前缀 + * + * @param packageName 包名称 + * @return 包前缀名称 + */ + public static String getPackagePrefix(String packageName) { + int lastIndex = packageName.lastIndexOf("."); + return StringUtils.substring(packageName, 0, lastIndex); + } + + /** + * 根据列类型获取导入包 + * + * @param genTable 业务表对象 + * @return 返回需要导入的包列表 + */ + public static HashSet getImportList(GenTable genTable) { + List columns = genTable.getColumns(); + HashSet importList = new HashSet<>(); + for (GenTableColumn column : columns) { + if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())) { + importList.add("java.util.Date"); + importList.add("com.fasterxml.jackson.annotation.JsonFormat"); + } else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) { + importList.add("java.math.BigDecimal"); + } else if (!column.isSuperColumn() && "imageUpload".equals(column.getHtmlType())) { + importList.add("org.dromara.common.translation.annotation.Translation"); + importList.add("org.dromara.common.translation.constant.TransConstant"); + } + } + return importList; + } + + /** + * 根据列类型获取字典组 + * + * @param genTable 业务表对象 + * @return 返回字典组 + */ + public static String getDicts(GenTable genTable) { + List columns = genTable.getColumns(); + Set dicts = new HashSet<>(); + addDicts(dicts, columns); + return StringUtils.join(dicts, ", "); + } + + /** + * 添加字典列表 + * + * @param dicts 字典列表 + * @param columns 列集合 + */ + public static void addDicts(Set dicts, List columns) { + for (GenTableColumn column : columns) { + if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny( + column.getHtmlType(), + new String[] { GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX })) { + dicts.add("'" + column.getDictType() + "'"); + } + } + } + + /** + * 获取权限前缀 + * + * @param moduleName 模块名称 + * @param businessName 业务名称 + * @return 返回权限前缀 + */ + public static String getPermissionPrefix(String moduleName, String businessName) { + return StringUtils.format("{}:{}", moduleName, businessName); + } + + /** + * 获取上级菜单ID字段 + * + * @param paramsObj 生成其他选项 + * @return 上级菜单ID字段 + */ + public static String getParentMenuId(Dict paramsObj) { + if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID) + && StringUtils.isNotEmpty(paramsObj.getStr(GenConstants.PARENT_MENU_ID))) { + return paramsObj.getStr(GenConstants.PARENT_MENU_ID); + } + return DEFAULT_PARENT_MENU_ID; + } + + /** + * 获取树编码 + * + * @param paramsObj 生成其他选项 + * @return 树编码 + */ + public static String getTreecode(Map paramsObj) { + if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_CODE)) { + return StringUtils.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_CODE))); + } + return StringUtils.EMPTY; + } + + /** + * 获取树父编码 + * + * @param paramsObj 生成其他选项 + * @return 树父编码 + */ + public static String getTreeParentCode(Dict paramsObj) { + if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) { + return StringUtils.toCamelCase(paramsObj.getStr(GenConstants.TREE_PARENT_CODE)); + } + return StringUtils.EMPTY; + } + + /** + * 获取树名称 + * + * @param paramsObj 生成其他选项 + * @return 树名称 + */ + public static String getTreeName(Dict paramsObj) { + if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_NAME)) { + return StringUtils.toCamelCase(paramsObj.getStr(GenConstants.TREE_NAME)); + } + return StringUtils.EMPTY; + } + + /** + * 获取需要在哪一列上面显示展开按钮 + * + * @param genTable 业务表对象 + * @return 展开按钮列序号 + */ + public static int getExpandColumn(GenTable genTable) { + String options = genTable.getOptions(); + Dict paramsObj = JsonUtils.parseMap(options); + String treeName = paramsObj.getStr(GenConstants.TREE_NAME); + int num = 0; + for (GenTableColumn column : genTable.getColumns()) { + if (column.isList()) { + num++; + String columnName = column.getColumnName(); + if (columnName.equals(treeName)) { + break; + } + } + } + return num; + } +} diff --git a/scripts/代码生成模板/vben5/api/index.ts.vm b/scripts/代码生成模板/vben5/api/index.ts.vm new file mode 100644 index 0000000..018c139 --- /dev/null +++ b/scripts/代码生成模板/vben5/api/index.ts.vm @@ -0,0 +1,69 @@ +import type { ${BusinessName}VO, ${BusinessName}Form, ${BusinessName}Query } from './model'; + +import type { ID, IDS } from '#/api/common'; +#if($tplCategory != 'tree') +import type { PageResult } from '#/api/common'; +#end + +import { commonExport } from '#/api/helper'; +import { requestClient } from '#/api/request'; + +/** +* 查询${functionName}列表 +* @param params +* @returns ${functionName}列表 +*/ +export function ${businessName}List(params?: ${BusinessName}Query) { + #if($tplCategory != 'tree') + return requestClient.get>('/${moduleName}/${businessName}/list', { params }); + #else + return requestClient.get<${BusinessName}VO[]>(`/${moduleName}/${businessName}/list`, { params }); + #end +} + +#if($tplCategory != 'tree') +/** + * 导出${functionName}列表 + * @param params + * @returns ${functionName}列表 + */ +export function ${businessName}Export(params?: ${BusinessName}Query) { + return commonExport('/${moduleName}/${businessName}/export', params ?? {}); +} +#end + +/** + * 查询${functionName}详情 + * @param ${pkColumn.javaField} id + * @returns ${functionName}详情 + */ +export function ${businessName}Info(${pkColumn.javaField}: ID) { + return requestClient.get<${BusinessName}VO>(`/${moduleName}/${businessName}/${${pkColumn.javaField}}`); +} + +/** + * 新增${functionName} + * @param data + * @returns void + */ +export function ${businessName}Add(data: ${BusinessName}Form) { + return requestClient.postWithMsg('/${moduleName}/${businessName}', data); +} + +/** + * 更新${functionName} + * @param data + * @returns void + */ +export function ${businessName}Update(data: ${BusinessName}Form) { + return requestClient.putWithMsg('/${moduleName}/${businessName}', data); +} + +/** + * 删除${functionName} + * @param ${pkColumn.javaField} id + * @returns void + */ +export function ${businessName}Remove(${pkColumn.javaField}: ID | IDS) { + return requestClient.deleteWithMsg(`/${moduleName}/${businessName}/${${pkColumn.javaField}}`); +} diff --git a/scripts/代码生成模板/vben5/api/model.d.ts.vm b/scripts/代码生成模板/vben5/api/model.d.ts.vm new file mode 100644 index 0000000..862c264 --- /dev/null +++ b/scripts/代码生成模板/vben5/api/model.d.ts.vm @@ -0,0 +1,56 @@ +import type { PageQuery, BaseEntity } from '#/api/common'; + +export interface ${BusinessName}VO { +#foreach ($column in $columns) +#if($column.list) + /** + * $column.columnComment + */ + $column.javaField:#if($column.javaField.indexOf("id") != -1 || $column.javaField.indexOf("Id") != -1) string | number; + #elseif($column.javaType == 'Long' || $column.javaType == 'Integer' || $column.javaType == 'Double' || $column.javaType == 'Float' || $column.javaType == 'BigDecimal') number; + #elseif($column.javaType == 'Boolean') boolean; + #else string; + #end +#end +#end +#if ($table.tree) + /** + * 子对象 + */ + children: ${BusinessName}VO[]; +#end +} + +export interface ${BusinessName}Form extends BaseEntity { +#foreach ($column in $columns) +#if($column.insert || $column.edit) + /** + * $column.columnComment + */ + $column.javaField?:#if($column.javaField.indexOf("id") != -1 || $column.javaField.indexOf("Id") != -1) string | number; + #elseif($column.javaType == 'Long' || $column.javaType == 'Integer' || $column.javaType == 'Double' || $column.javaType == 'Float' || $column.javaType == 'BigDecimal') number; + #elseif($column.javaType == 'Boolean') boolean; + #else string; + #end +#end +#end +} + +export interface ${BusinessName}Query #if(!${treeCode})extends PageQuery #end{ +#foreach ($column in $columns) +#if($column.query) + /** + * $column.columnComment + */ + $column.javaField?:#if($column.javaField.indexOf("id") != -1 || $column.javaField.indexOf("Id") != -1) string | number; + #elseif($column.javaType == 'Long' || $column.javaType == 'Integer' || $column.javaType == 'Double' || $column.javaType == 'Float' || $column.javaType == 'BigDecimal') number; + #elseif($column.javaType == 'Boolean') boolean; + #else string; + #end +#end +#end + /** + * 日期范围参数 + */ + params?: any; +} diff --git a/scripts/代码生成模板/vben5/views/data.ts.vm b/scripts/代码生成模板/vben5/views/data.ts.vm new file mode 100644 index 0000000..73091a2 --- /dev/null +++ b/scripts/代码生成模板/vben5/views/data.ts.vm @@ -0,0 +1,221 @@ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +#if(${dicts} != '') +import { getDictOptions } from '#/utils/dict'; +import { renderDict } from '#/utils/render'; +#end + +export const querySchema: FormSchemaGetter = () => [ + #foreach($column in $columns) + #if($column.query) + #if($column.dictType) + #set($dictType=$column.dictType) + #else + #set($dictType="") + #end + #set($parentheseIndex=$column.columnComment.indexOf("(")) + #if($parentheseIndex != -1) + #set($comment=$column.columnComment.substring(0, $parentheseIndex)) + #else + #set($comment=$column.columnComment) + #end + #if($column.htmlType == "input") + #set($component="Input") + #elseif($column.htmlType == "textarea") + #set($component="Textarea") + #elseif($column.htmlType == "select") + #set($component="Select") + #elseif($column.htmlType == "radio") + #set($component="RadioGroup") + #elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN") + #set($component="DatePicker") + #elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN") + #set($component="RangePicker") + #else + #set($component="Input") + #end + { + component: '${component}', + #if($component == "Select" || $component == "RadioGroup") + componentProps: { + #if($dictType != "") + // 可选从DictEnum中获取 DictEnum.${dictType.toUpperCase()} 便于维护 + options: getDictOptions('$dictType'), + #end + #if($component == "RadioGroup") + buttonStyle: 'solid', + optionType: 'button', + #end + }, + #elseif($component == "DatePicker" || $component == "RangePicker") + componentProps: { + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'YYYY-MM-DD HH:mm:ss', + }, + #end + fieldName: '${column.javaField}', + label: '${comment}', + }, + #end + #end +]; + +// 需要使用i18n注意这里要改成getter形式 否则切换语言不会刷新 +// export const columns: () => VxeGridProps['columns'] = () => [ +export const columns: VxeGridProps['columns'] = [ + #if($tplCategory != 'tree') + { type: 'checkbox', width: 60 }, + #end + #foreach($column in $columns) + #if($column.list) + #if($column.dictType) + #set($dictType=$column.dictType) + #else + #set($dictType="") + #end + #set($parentheseIndex=$column.columnComment.indexOf("(")) + #if($parentheseIndex != -1) + #set($comment=$column.columnComment.substring(0, $parentheseIndex)) + #else + #set($comment=$column.columnComment) + #end + { + title: '${comment}', + field: '${column.javaField}', + #if( $foreach.count == 1 && $tplCategory == 'tree') + treeNode: true, + #end + #if($dictType != "") + slots: { + default: ({ row }) => { + // 可选从DictEnum中获取 DictEnum.${dictType.toUpperCase()} 便于维护 + return row.${column.javaField}?renderDict(row.${column.javaField}, '$dictType'):''; + }, + }, + #end + }, + #end + #end + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '操作', + width: 180, + }, +]; + +#if($formComponent == "useForm") +export const ${popupComponent}Schema: FormSchemaGetter = () => [ + #foreach($column in $columns) + #if($column.edit) + #if($column.dictType) + #set($dictType=$column.dictType) + #else + #set($dictType="") + #end + #set($parentheseIndex=$column.columnComment.indexOf("(")) + #if($parentheseIndex != -1) + #set($comment=$column.columnComment.substring(0, $parentheseIndex)) + #else + #set($comment=$column.columnComment) + #end + #if($column.htmlType == "input") + #set($component="Input") + #elseif($column.htmlType == "textarea") + #set($component="Textarea") + #elseif($column.htmlType == "select") + #set($component="Select") + #elseif($column.htmlType == "radio") + #set($component="RadioGroup") + #elseif($column.htmlType == "checkbox") + #set($component="Checkbox") + #elseif($column.htmlType == "imageUpload") + #set($component="ImageUpload") + #elseif($column.htmlType == "fileUpload") + #set($component="FileUpload") + #elseif($column.htmlType == "editor") + #set($component="RichTextarea") + #elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN") + #set($component="DatePicker") + #elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN") + #set($component="RangePicker") + #else + #set($component="Input") + #end + #if($column.required && $column.pk == false) + #set($required='true') + #else + #set($required='false') + #end + { + label: '${comment}', + fieldName: '${column.javaField}', + #if("" != $treeParentCode && $column.javaField == $treeParentCode) + component: 'TreeSelect', + #else + component: '${component}', + #end + #if($component == "DatePicker" || $component == "RangePicker") + componentProps: { + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'YYYY-MM-DD HH:mm:ss', + }, + #elseif($component == "ImageUpload") + componentProps: { + // accept: ['jpg'], // 不支持type/*的写法 支持拓展名(不带.) 文件头(image/png这种) + // maxNumber: 1, // 最大上传文件数 默认为1 为1会绑定为string而非string[]类型 + // resultField: 'url', // 上传成功后返回的字段名 默认url 可选['ossId', 'url', 'fileName'] + }, + #elseif($component == "FileUpload") + /** + * 注意这里获取为数组 需要自行定义回显/提交 + * 文件上传还在demo阶段 可能有重大改动! + */ + componentProps: { + // accept: ['xlsx'], // 不支持type/*的写法 建议使用拓展名(不带.) + // maxNumber: 1, // 最大上传文件数 + // resultField: 'url', // 上传成功后返回的字段名 默认url 可选['ossId', 'url', 'fileName'] + }, + #elseif($component == "RichTextarea") + componentProps: { + // options: { + // readyonly: false, // 是否只读 + // } + // width: '100%', // 宽度 + // showImageUpload: true, // 是否显示图片上传 + // height: 400 // 高度 默认400 + }, + #elseif($component == "Select" || $component == "RadioGroup" || $component == "Checkbox") + componentProps: { + #if($dictType != "") + // 可选从DictEnum中获取 DictEnum.${dictType.toUpperCase()} 便于维护 + options: getDictOptions('$dictType'), + #end + #if($component == "RadioGroup") + buttonStyle: 'solid', + optionType: 'button', + #end + }, + #end + #if(${column.pk}) + dependencies: { + show: () => false, + triggerFields: [''], + }, + #end + #if($required == 'true' && $column.pk == false) + #if($component == "Select" || $component == "RadioGroup" || $component == "Checkbox") + rules: 'selectRequired', + #else + rules: 'required', + #end + #end + }, + #end + #end +]; +#end diff --git a/scripts/代码生成模板/vben5/views/index_vben.vue.vm b/scripts/代码生成模板/vben5/views/index_vben.vue.vm new file mode 100644 index 0000000..c0afcf8 --- /dev/null +++ b/scripts/代码生成模板/vben5/views/index_vben.vue.vm @@ -0,0 +1,186 @@ + + + diff --git a/scripts/代码生成模板/vben5/views/index_vben_tree.vue.vm b/scripts/代码生成模板/vben5/views/index_vben_tree.vue.vm new file mode 100644 index 0000000..cf7ad56 --- /dev/null +++ b/scripts/代码生成模板/vben5/views/index_vben_tree.vue.vm @@ -0,0 +1,152 @@ + + + diff --git a/scripts/代码生成模板/vben5/views/popup.vue.vm b/scripts/代码生成模板/vben5/views/popup.vue.vm new file mode 100644 index 0000000..4c1889b --- /dev/null +++ b/scripts/代码生成模板/vben5/views/popup.vue.vm @@ -0,0 +1,347 @@ +#if($formComponent == "useForm") + + + +#else + + + +