feat: editor script setup

This commit is contained in:
Dominik Pschenitschni 2022-10-03 12:18:20 +02:00
parent cbec1f24aa
commit db627ed28a
No known key found for this signature in database
GPG key ID: B257AC0149F43A77
7 changed files with 289 additions and 279 deletions

View file

@ -69,6 +69,7 @@
"@faker-js/faker": "7.5.0", "@faker-js/faker": "7.5.0",
"@types/dompurify": "2.3.4", "@types/dompurify": "2.3.4",
"@types/flexsearch": "0.7.3", "@types/flexsearch": "0.7.3",
"@types/marked": "4.0.7",
"@types/node": "16.11.64", "@types/node": "16.11.64",
"@typescript-eslint/eslint-plugin": "5.39.0", "@typescript-eslint/eslint-plugin": "5.39.0",
"@typescript-eslint/parser": "5.39.0", "@typescript-eslint/parser": "5.39.0",

View file

@ -1,4 +1,4 @@
lockfileVersion: 5.3 lockfileVersion: 5.4
specifiers: specifiers:
'@4tw/cypress-drag-drop': 2.2.1 '@4tw/cypress-drag-drop': 2.2.1
@ -17,6 +17,7 @@ specifiers:
'@types/flexsearch': 0.7.3 '@types/flexsearch': 0.7.3
'@types/is-touch-device': 1.0.0 '@types/is-touch-device': 1.0.0
'@types/lodash.clonedeep': 4.5.7 '@types/lodash.clonedeep': 4.5.7
'@types/marked': 4.0.7
'@types/node': 16.11.64 '@types/node': 16.11.64
'@types/sortablejs': 1.15.0 '@types/sortablejs': 1.15.0
'@typescript-eslint/eslint-plugin': 5.39.0 '@typescript-eslint/eslint-plugin': 5.39.0
@ -86,7 +87,7 @@ dependencies:
'@fortawesome/fontawesome-svg-core': 6.2.0 '@fortawesome/fontawesome-svg-core': 6.2.0
'@fortawesome/free-regular-svg-icons': 6.2.0 '@fortawesome/free-regular-svg-icons': 6.2.0
'@fortawesome/free-solid-svg-icons': 6.2.0 '@fortawesome/free-solid-svg-icons': 6.2.0
'@fortawesome/vue-fontawesome': 3.0.1_21e6b971ab1cbc4f6c07645df616013e '@fortawesome/vue-fontawesome': 3.0.1_ehtls4nlds6e63ahmro7mfqbhy
'@github/hotkey': 2.0.1 '@github/hotkey': 2.0.1
'@kyvg/vue3-notification': 2.4.1_vue@3.2.40 '@kyvg/vue3-notification': 2.4.1_vue@3.2.40
'@sentry/tracing': 7.14.1 '@sentry/tracing': 7.14.1
@ -95,7 +96,7 @@ dependencies:
'@types/lodash.clonedeep': 4.5.7 '@types/lodash.clonedeep': 4.5.7
'@types/sortablejs': 1.15.0 '@types/sortablejs': 1.15.0
'@vueuse/core': 9.3.0_vue@3.2.40 '@vueuse/core': 9.3.0_vue@3.2.40
'@vueuse/router': 9.3.0_vue-router@4.1.5+vue@3.2.40 '@vueuse/router': 9.3.0_c7eza3xvlyb4mo6qeit5ggeo6u
axios: 0.27.2 axios: 0.27.2
blurhash: 2.0.3 blurhash: 2.0.3
bulma-css-variables: 0.9.33 bulma-css-variables: 0.9.33
@ -112,7 +113,7 @@ dependencies:
lodash.debounce: 4.0.8 lodash.debounce: 4.0.8
marked: 4.1.1 marked: 4.1.1
minimist: 1.2.6 minimist: 1.2.6
pinia: 2.0.22_typescript@4.8.4+vue@3.2.40 pinia: 2.0.22_bfjwoga25wxjazzogo7o372nwq
register-service-worker: 1.7.2 register-service-worker: 1.7.2
snake-case: 3.0.4 snake-case: 3.0.4
sortablejs: 1.15.0 sortablejs: 1.15.0
@ -134,12 +135,13 @@ devDependencies:
'@faker-js/faker': 7.5.0 '@faker-js/faker': 7.5.0
'@types/dompurify': 2.3.4 '@types/dompurify': 2.3.4
'@types/flexsearch': 0.7.3 '@types/flexsearch': 0.7.3
'@types/marked': 4.0.7
'@types/node': 16.11.64 '@types/node': 16.11.64
'@typescript-eslint/eslint-plugin': 5.39.0_be048b79d04b908735dc19ebba1dbd66 '@typescript-eslint/eslint-plugin': 5.39.0_xyciw6oqjoiiono4dhv3uhn5my
'@typescript-eslint/parser': 5.39.0_eslint@8.24.0+typescript@4.8.4 '@typescript-eslint/parser': 5.39.0_ypn2ylkkyfa5i233caldtndbqa
'@vitejs/plugin-legacy': 2.2.0_terser@5.10.0+vite@3.1.4 '@vitejs/plugin-legacy': 2.2.0_terser@5.10.0+vite@3.1.4
'@vitejs/plugin-vue': 3.1.2_vite@3.1.4+vue@3.2.40 '@vitejs/plugin-vue': 3.1.2_vite@3.1.4+vue@3.2.40
'@vue/eslint-config-typescript': 11.0.2_ab5ac96ab635ce1fa6bba69a2b0ff7cb '@vue/eslint-config-typescript': 11.0.2_vnnms2vwgxhb7jv3u2ncwd7xzm
'@vue/test-utils': 2.1.0_vue@3.2.40 '@vue/test-utils': 2.1.0_vue@3.2.40
'@vue/tsconfig': 0.1.3_@types+node@16.11.64 '@vue/tsconfig': 0.1.3_@types+node@16.11.64
autoprefixer: 10.4.12_postcss@8.4.17 autoprefixer: 10.4.12_postcss@8.4.17
@ -159,12 +161,12 @@ devDependencies:
sass: 1.55.0 sass: 1.55.0
typescript: 4.8.4 typescript: 4.8.4
vite: 3.1.4_sass@1.55.0+terser@5.10.0 vite: 3.1.4_sass@1.55.0+terser@5.10.0
vite-plugin-pwa: 0.13.1_vite@3.1.4 vite-plugin-pwa: 0.13.1_bhe5iaipiq3lmbaxwdxgnnn2gq
vite-svg-loader: 3.6.0 vite-svg-loader: 3.6.0
vitest: 0.23.4_ddc85b0b7a78b2cfae025c8e16b7f605 vitest: 0.23.4_3xefwc32pczm7lqclshbnn7wau
vue-tsc: 0.40.13_typescript@4.8.4 vue-tsc: 0.40.13_typescript@4.8.4
wait-on: 6.0.1 wait-on: 6.0.1
workbox-cli: 6.5.4 workbox-cli: 6.5.4_acorn@8.8.0
packages: packages:
@ -1392,7 +1394,7 @@ packages:
peerDependencies: peerDependencies:
postcss: ^8.2 postcss: ^8.2
dependencies: dependencies:
'@csstools/selector-specificity': 2.0.2_cd239324a5aeb6e3cee0fb61f6a33448 '@csstools/selector-specificity': 2.0.2_zurzgjffv23ohtxa7nq7nizuja
postcss: 8.4.17 postcss: 8.4.17
postcss-selector-parser: 6.0.10 postcss-selector-parser: 6.0.10
dev: true dev: true
@ -1445,7 +1447,7 @@ packages:
peerDependencies: peerDependencies:
postcss: ^8.2 postcss: ^8.2
dependencies: dependencies:
'@csstools/selector-specificity': 2.0.2_cd239324a5aeb6e3cee0fb61f6a33448 '@csstools/selector-specificity': 2.0.2_zurzgjffv23ohtxa7nq7nizuja
postcss: 8.4.17 postcss: 8.4.17
postcss-selector-parser: 6.0.10 postcss-selector-parser: 6.0.10
dev: true dev: true
@ -1530,7 +1532,7 @@ packages:
postcss: 8.4.17 postcss: 8.4.17
dev: true dev: true
/@csstools/selector-specificity/2.0.2_cd239324a5aeb6e3cee0fb61f6a33448: /@csstools/selector-specificity/2.0.2_zurzgjffv23ohtxa7nq7nizuja:
resolution: {integrity: sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==} resolution: {integrity: sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==}
engines: {node: ^12 || ^14 || >=16} engines: {node: ^12 || ^14 || >=16}
peerDependencies: peerDependencies:
@ -1679,7 +1681,7 @@ packages:
'@fortawesome/fontawesome-common-types': 6.2.0 '@fortawesome/fontawesome-common-types': 6.2.0
dev: false dev: false
/@fortawesome/vue-fontawesome/3.0.1_21e6b971ab1cbc4f6c07645df616013e: /@fortawesome/vue-fontawesome/3.0.1_ehtls4nlds6e63ahmro7mfqbhy:
resolution: {integrity: sha512-CdXZJoCS+aEPec26ZP7hWWU3SaJlQPZSCGdgpQ2qGl2HUmtUUNrI3zC4XWdn1JUmh3t5OuDeRG1qB4eGRNSD4A==} resolution: {integrity: sha512-CdXZJoCS+aEPec26ZP7hWWU3SaJlQPZSCGdgpQ2qGl2HUmtUUNrI3zC4XWdn1JUmh3t5OuDeRG1qB4eGRNSD4A==}
peerDependencies: peerDependencies:
'@fortawesome/fontawesome-svg-core': ~1 || ~6 '@fortawesome/fontawesome-svg-core': ~1 || ~6
@ -1917,7 +1919,7 @@ packages:
strip-ansi: 7.0.1 strip-ansi: 7.0.1
supports-color: 9.2.1 supports-color: 9.2.1
tmp-promise: 3.0.3 tmp-promise: 3.0.3
ts-node: 10.8.1_56922d2c3e8466316d745c2e9b343672 ts-node: 10.8.1_k2jc2lb6qrtdc3lulqxjwnbwoi
typescript: 4.8.4 typescript: 4.8.4
update-notifier: 5.1.0 update-notifier: 5.1.0
uuid: 8.3.2 uuid: 8.3.2
@ -2605,7 +2607,7 @@ packages:
resolution: {integrity: sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==} resolution: {integrity: sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==}
dev: false dev: false
/@rollup/plugin-babel/5.3.0_@babel+core@7.17.2+rollup@2.79.1: /@rollup/plugin-babel/5.3.0_pf2mys4p2khuj2gysypj3zzjia:
resolution: {integrity: sha512-9uIC8HZOnVLrLHxayq/PTzw+uS25E14KPUBh5ktF+18Mjo5yK0ToMMx6epY0uEgkjwJw0aBW4x2horYXh8juWw==} resolution: {integrity: sha512-9uIC8HZOnVLrLHxayq/PTzw+uS25E14KPUBh5ktF+18Mjo5yK0ToMMx6epY0uEgkjwJw0aBW4x2horYXh8juWw==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
peerDependencies: peerDependencies:
@ -2989,7 +2991,6 @@ packages:
/@types/marked/4.0.7: /@types/marked/4.0.7:
resolution: {integrity: sha512-eEAhnz21CwvKVW+YvRvcTuFKNU9CV1qH+opcgVK3pIMI6YZzDm6gc8o2vHjldFk6MGKt5pueSB7IOpvpx5Qekw==} resolution: {integrity: sha512-eEAhnz21CwvKVW+YvRvcTuFKNU9CV1qH+opcgVK3pIMI6YZzDm6gc8o2vHjldFk6MGKt5pueSB7IOpvpx5Qekw==}
dev: false
/@types/minimatch/3.0.5: /@types/minimatch/3.0.5:
resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==} resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==}
@ -3104,7 +3105,7 @@ packages:
dev: true dev: true
optional: true optional: true
/@typescript-eslint/eslint-plugin/5.39.0_be048b79d04b908735dc19ebba1dbd66: /@typescript-eslint/eslint-plugin/5.39.0_xyciw6oqjoiiono4dhv3uhn5my:
resolution: {integrity: sha512-xVfKOkBm5iWMNGKQ2fwX5GVgBuHmZBO1tCRwXmY5oAIsPscfwm2UADDuNB8ZVYCtpQvJK4xpjrK7jEhcJ0zY9A==} resolution: {integrity: sha512-xVfKOkBm5iWMNGKQ2fwX5GVgBuHmZBO1tCRwXmY5oAIsPscfwm2UADDuNB8ZVYCtpQvJK4xpjrK7jEhcJ0zY9A==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies: peerDependencies:
@ -3115,10 +3116,10 @@ packages:
typescript: typescript:
optional: true optional: true
dependencies: dependencies:
'@typescript-eslint/parser': 5.39.0_eslint@8.24.0+typescript@4.8.4 '@typescript-eslint/parser': 5.39.0_ypn2ylkkyfa5i233caldtndbqa
'@typescript-eslint/scope-manager': 5.39.0 '@typescript-eslint/scope-manager': 5.39.0
'@typescript-eslint/type-utils': 5.39.0_eslint@8.24.0+typescript@4.8.4 '@typescript-eslint/type-utils': 5.39.0_ypn2ylkkyfa5i233caldtndbqa
'@typescript-eslint/utils': 5.39.0_eslint@8.24.0+typescript@4.8.4 '@typescript-eslint/utils': 5.39.0_ypn2ylkkyfa5i233caldtndbqa
debug: 4.3.4 debug: 4.3.4
eslint: 8.24.0 eslint: 8.24.0
ignore: 5.2.0 ignore: 5.2.0
@ -3130,7 +3131,7 @@ packages:
- supports-color - supports-color
dev: true dev: true
/@typescript-eslint/parser/5.39.0_eslint@8.24.0+typescript@4.8.4: /@typescript-eslint/parser/5.39.0_ypn2ylkkyfa5i233caldtndbqa:
resolution: {integrity: sha512-PhxLjrZnHShe431sBAGHaNe6BDdxAASDySgsBCGxcBecVCi8NQWxQZMcizNA4g0pN51bBAn/FUfkWG3SDVcGlA==} resolution: {integrity: sha512-PhxLjrZnHShe431sBAGHaNe6BDdxAASDySgsBCGxcBecVCi8NQWxQZMcizNA4g0pN51bBAn/FUfkWG3SDVcGlA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies: peerDependencies:
@ -3158,7 +3159,7 @@ packages:
'@typescript-eslint/visitor-keys': 5.39.0 '@typescript-eslint/visitor-keys': 5.39.0
dev: true dev: true
/@typescript-eslint/type-utils/5.39.0_eslint@8.24.0+typescript@4.8.4: /@typescript-eslint/type-utils/5.39.0_ypn2ylkkyfa5i233caldtndbqa:
resolution: {integrity: sha512-KJHJkOothljQWzR3t/GunL0TPKY+fGJtnpl+pX+sJ0YiKTz3q2Zr87SGTmFqsCMFrLt5E0+o+S6eQY0FAXj9uA==} resolution: {integrity: sha512-KJHJkOothljQWzR3t/GunL0TPKY+fGJtnpl+pX+sJ0YiKTz3q2Zr87SGTmFqsCMFrLt5E0+o+S6eQY0FAXj9uA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies: peerDependencies:
@ -3169,7 +3170,7 @@ packages:
optional: true optional: true
dependencies: dependencies:
'@typescript-eslint/typescript-estree': 5.39.0_typescript@4.8.4 '@typescript-eslint/typescript-estree': 5.39.0_typescript@4.8.4
'@typescript-eslint/utils': 5.39.0_eslint@8.24.0+typescript@4.8.4 '@typescript-eslint/utils': 5.39.0_ypn2ylkkyfa5i233caldtndbqa
debug: 4.3.4 debug: 4.3.4
eslint: 8.24.0 eslint: 8.24.0
tsutils: 3.21.0_typescript@4.8.4 tsutils: 3.21.0_typescript@4.8.4
@ -3183,7 +3184,7 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true dev: true
/@typescript-eslint/typescript-estree/5.39.0_f1deb5be19df0fe0ff039530117daddf: /@typescript-eslint/typescript-estree/5.39.0_6hpllpqz34h6b7ydsuybc7nn34:
resolution: {integrity: sha512-qLFQP0f398sdnogJoLtd43pUgB18Q50QSA+BTE5h3sUxySzbWDpTSdgt4UyxNSozY/oDK2ta6HVAzvGgq8JYnA==} resolution: {integrity: sha512-qLFQP0f398sdnogJoLtd43pUgB18Q50QSA+BTE5h3sUxySzbWDpTSdgt4UyxNSozY/oDK2ta6HVAzvGgq8JYnA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies: peerDependencies:
@ -3225,7 +3226,7 @@ packages:
- supports-color - supports-color
dev: true dev: true
/@typescript-eslint/utils/5.39.0_eslint@8.24.0+typescript@4.8.4: /@typescript-eslint/utils/5.39.0_ypn2ylkkyfa5i233caldtndbqa:
resolution: {integrity: sha512-+DnY5jkpOpgj+EBtYPyHRjXampJfC0yUZZzfzLuUWVZvCuKqSdJVC8UhdWipIw7VKNTfwfAPiOWzYkAwuIhiAg==} resolution: {integrity: sha512-+DnY5jkpOpgj+EBtYPyHRjXampJfC0yUZZzfzLuUWVZvCuKqSdJVC8UhdWipIw7VKNTfwfAPiOWzYkAwuIhiAg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies: peerDependencies:
@ -3303,7 +3304,7 @@ packages:
magic-string: 0.26.3 magic-string: 0.26.3
regenerator-runtime: 0.13.9 regenerator-runtime: 0.13.9
systemjs: 6.12.6 systemjs: 6.12.6
terser: 5.10.0 terser: 5.10.0_acorn@8.8.0
vite: 3.1.4_sass@1.55.0+terser@5.10.0 vite: 3.1.4_sass@1.55.0+terser@5.10.0
dev: true dev: true
@ -3432,7 +3433,7 @@ packages:
resolution: {integrity: sha512-OEgAMeQXvCoJ+1x8WyQuVZzFo0wcyCmUR3baRVLmKBo1LmYZWMlRiXlux5jd0fqVJu6PfDbOrZItVqUEzLobeQ==} resolution: {integrity: sha512-OEgAMeQXvCoJ+1x8WyQuVZzFo0wcyCmUR3baRVLmKBo1LmYZWMlRiXlux5jd0fqVJu6PfDbOrZItVqUEzLobeQ==}
dev: false dev: false
/@vue/eslint-config-typescript/11.0.2_ab5ac96ab635ce1fa6bba69a2b0ff7cb: /@vue/eslint-config-typescript/11.0.2_vnnms2vwgxhb7jv3u2ncwd7xzm:
resolution: {integrity: sha512-EiKud1NqlWmSapBFkeSrE994qpKx7/27uCGnhdqzllYDpQZroyX/O6bwjEpeuyKamvLbsGdO6PMR2faIf+zFnw==} resolution: {integrity: sha512-EiKud1NqlWmSapBFkeSrE994qpKx7/27uCGnhdqzllYDpQZroyX/O6bwjEpeuyKamvLbsGdO6PMR2faIf+zFnw==}
engines: {node: ^14.17.0 || >=16.0.0} engines: {node: ^14.17.0 || >=16.0.0}
peerDependencies: peerDependencies:
@ -3443,8 +3444,8 @@ packages:
typescript: typescript:
optional: true optional: true
dependencies: dependencies:
'@typescript-eslint/eslint-plugin': 5.39.0_be048b79d04b908735dc19ebba1dbd66 '@typescript-eslint/eslint-plugin': 5.39.0_xyciw6oqjoiiono4dhv3uhn5my
'@typescript-eslint/parser': 5.39.0_eslint@8.24.0+typescript@4.8.4 '@typescript-eslint/parser': 5.39.0_ypn2ylkkyfa5i233caldtndbqa
eslint: 8.24.0 eslint: 8.24.0
eslint-plugin-vue: 9.6.0_eslint@8.24.0 eslint-plugin-vue: 9.6.0_eslint@8.24.0
typescript: 4.8.4 typescript: 4.8.4
@ -3551,7 +3552,7 @@ packages:
resolution: {integrity: sha512-GnnfjbzIPJIh9ngL9s9oGU1+Hx/h5/KFqTfJykzh/1xjaHkedV9g0MASpdmPZIP+ynNhKAcEfA6g5i8KXwtoMA==} resolution: {integrity: sha512-GnnfjbzIPJIh9ngL9s9oGU1+Hx/h5/KFqTfJykzh/1xjaHkedV9g0MASpdmPZIP+ynNhKAcEfA6g5i8KXwtoMA==}
dev: false dev: false
/@vueuse/router/9.3.0_vue-router@4.1.5+vue@3.2.40: /@vueuse/router/9.3.0_c7eza3xvlyb4mo6qeit5ggeo6u:
resolution: {integrity: sha512-UFN2MFciprH21oYsAgNHeDJ4Bd86HpRm9gximSN8j6h4fc2aa62fvfhprfHqdTxYAcgcGkMwcc9TO75jOvr8gg==} resolution: {integrity: sha512-UFN2MFciprH21oYsAgNHeDJ4Bd86HpRm9gximSN8j6h4fc2aa62fvfhprfHqdTxYAcgcGkMwcc9TO75jOvr8gg==}
peerDependencies: peerDependencies:
vue-router: '>=4.0.0-rc.1' vue-router: '>=4.0.0-rc.1'
@ -5572,7 +5573,7 @@ packages:
resolution: {integrity: sha512-lR78AugfUSBojwlSRZBeEqQ1l8LI7rbxOl1qTUnGLcjZQDjZmrZCb7R46rK8U8B5WzFvJrxa7fEBA8FoD/n5fA==} resolution: {integrity: sha512-lR78AugfUSBojwlSRZBeEqQ1l8LI7rbxOl1qTUnGLcjZQDjZmrZCb7R46rK8U8B5WzFvJrxa7fEBA8FoD/n5fA==}
engines: {node: ^12.20.0 || ^14.14.0 || >=16.0.0} engines: {node: ^12.20.0 || ^14.14.0 || >=16.0.0}
dependencies: dependencies:
'@typescript-eslint/typescript-estree': 5.39.0_f1deb5be19df0fe0ff039530117daddf '@typescript-eslint/typescript-estree': 5.39.0_6hpllpqz34h6b7ydsuybc7nn34
ast-module-types: 3.0.0 ast-module-types: 3.0.0
node-source-walk: 5.0.0 node-source-walk: 5.0.0
typescript: 4.8.4 typescript: 4.8.4
@ -10088,7 +10089,7 @@ packages:
engines: {node: '>=6'} engines: {node: '>=6'}
dev: true dev: true
/pinia/2.0.22_typescript@4.8.4+vue@3.2.40: /pinia/2.0.22_bfjwoga25wxjazzogo7o372nwq:
resolution: {integrity: sha512-u+b8/BC+tmvo3ACbYO2w5NfxHWFOjvvw9DQnyT0dW8aUMCPRQT5QnfZ5R5W2MzZBMTeZRMQI7V/QFbafmM9QHw==} resolution: {integrity: sha512-u+b8/BC+tmvo3ACbYO2w5NfxHWFOjvvw9DQnyT0dW8aUMCPRQT5QnfZ5R5W2MzZBMTeZRMQI7V/QFbafmM9QHw==}
peerDependencies: peerDependencies:
'@vue/composition-api': ^1.4.0 '@vue/composition-api': ^1.4.0
@ -10331,7 +10332,7 @@ packages:
peerDependencies: peerDependencies:
postcss: ^8.2 postcss: ^8.2
dependencies: dependencies:
'@csstools/selector-specificity': 2.0.2_cd239324a5aeb6e3cee0fb61f6a33448 '@csstools/selector-specificity': 2.0.2_zurzgjffv23ohtxa7nq7nizuja
postcss: 8.4.17 postcss: 8.4.17
postcss-selector-parser: 6.0.10 postcss-selector-parser: 6.0.10
dev: true dev: true
@ -11047,7 +11048,7 @@ packages:
glob: 7.2.0 glob: 7.2.0
dev: true dev: true
/rollup-plugin-terser/7.0.2_rollup@2.79.1: /rollup-plugin-terser/7.0.2_acorn@8.8.0+rollup@2.79.1:
resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==}
peerDependencies: peerDependencies:
rollup: ^2.0.0 rollup: ^2.0.0
@ -11056,7 +11057,9 @@ packages:
jest-worker: 26.6.2 jest-worker: 26.6.2
rollup: 2.79.1 rollup: 2.79.1
serialize-javascript: 4.0.0 serialize-javascript: 4.0.0
terser: 5.10.0 terser: 5.10.0_acorn@8.8.0
transitivePeerDependencies:
- acorn
dev: true dev: true
/rollup-plugin-visualizer/5.8.2_rollup@2.79.1: /rollup-plugin-visualizer/5.8.2_rollup@2.79.1:
@ -11938,10 +11941,12 @@ packages:
supports-hyperlinks: 2.2.0 supports-hyperlinks: 2.2.0
dev: true dev: true
/terser/5.10.0: /terser/5.10.0_acorn@8.8.0:
resolution: {integrity: sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==} resolution: {integrity: sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==}
engines: {node: '>=10'} engines: {node: '>=10'}
hasBin: true hasBin: true
peerDependencies:
acorn: ^8.5.0
peerDependenciesMeta: peerDependenciesMeta:
acorn: acorn:
optional: true optional: true
@ -12147,7 +12152,7 @@ packages:
resolution: {integrity: sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==} resolution: {integrity: sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==}
dev: true dev: true
/ts-node/10.8.1_56922d2c3e8466316d745c2e9b343672: /ts-node/10.8.1_k2jc2lb6qrtdc3lulqxjwnbwoi:
resolution: {integrity: sha512-Wwsnao4DQoJsN034wePSg5nZiw4YKXf56mPIAeD6wVmiv+RytNSWqc2f3fKvcUoV+Yn2+yocD71VOfQHbmVX4g==} resolution: {integrity: sha512-Wwsnao4DQoJsN034wePSg5nZiw4YKXf56mPIAeD6wVmiv+RytNSWqc2f3fKvcUoV+Yn2+yocD71VOfQHbmVX4g==}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
@ -12545,20 +12550,21 @@ packages:
extsprintf: 1.3.0 extsprintf: 1.3.0
dev: true dev: true
/vite-plugin-pwa/0.13.1_vite@3.1.4: /vite-plugin-pwa/0.13.1_bhe5iaipiq3lmbaxwdxgnnn2gq:
resolution: {integrity: sha512-NR3dIa+o2hzlzo4lF4Gu0cYvoMjSw2DdRc6Epw1yjmCqWaGuN86WK9JqZie4arNlE1ZuWT3CLiMdiX5wcmmUmg==} resolution: {integrity: sha512-NR3dIa+o2hzlzo4lF4Gu0cYvoMjSw2DdRc6Epw1yjmCqWaGuN86WK9JqZie4arNlE1ZuWT3CLiMdiX5wcmmUmg==}
peerDependencies: peerDependencies:
vite: ^3.1.0 vite: ^3.1.0
workbox-build: ^6.5.4
workbox-window: ^6.5.4
dependencies: dependencies:
debug: 4.3.4 debug: 4.3.4
fast-glob: 3.2.11 fast-glob: 3.2.11
pretty-bytes: 6.0.0 pretty-bytes: 6.0.0
rollup: 2.79.1 rollup: 2.79.1
vite: 3.1.4_sass@1.55.0+terser@5.10.0 vite: 3.1.4_sass@1.55.0+terser@5.10.0
workbox-build: 6.5.4 workbox-build: 6.5.4_acorn@8.8.0
workbox-window: 6.5.4 workbox-window: 6.5.4
transitivePeerDependencies: transitivePeerDependencies:
- '@types/babel__core'
- supports-color - supports-color
dev: true dev: true
@ -12593,12 +12599,12 @@ packages:
resolve: 1.22.1 resolve: 1.22.1
rollup: 2.78.0 rollup: 2.78.0
sass: 1.55.0 sass: 1.55.0
terser: 5.10.0 terser: 5.10.0_acorn@8.8.0
optionalDependencies: optionalDependencies:
fsevents: 2.3.2 fsevents: 2.3.2
dev: true dev: true
/vitest/0.23.4_ddc85b0b7a78b2cfae025c8e16b7f605: /vitest/0.23.4_3xefwc32pczm7lqclshbnn7wau:
resolution: {integrity: sha512-iukBNWqQAv8EKDBUNntspLp9SfpaVFbmzmM0sNcnTxASQZMzRw3PsM6DMlsHiI+I6GeO5/sYDg3ecpC+SNFLrQ==} resolution: {integrity: sha512-iukBNWqQAv8EKDBUNntspLp9SfpaVFbmzmM0sNcnTxASQZMzRw3PsM6DMlsHiI+I6GeO5/sYDg3ecpC+SNFLrQ==}
engines: {node: '>=v14.16.0'} engines: {node: '>=v14.16.0'}
hasBin: true hasBin: true
@ -12912,7 +12918,7 @@ packages:
workbox-core: 6.5.4 workbox-core: 6.5.4
dev: true dev: true
/workbox-build/6.5.4: /workbox-build/6.5.4_acorn@8.8.0:
resolution: {integrity: sha512-kgRevLXEYvUW9WS4XoziYqZ8Q9j/2ziJYEtTrjdz5/L/cTUa2XfyMP2i7c3p34lgqJ03+mTiz13SdFef2POwbA==} resolution: {integrity: sha512-kgRevLXEYvUW9WS4XoziYqZ8Q9j/2ziJYEtTrjdz5/L/cTUa2XfyMP2i7c3p34lgqJ03+mTiz13SdFef2POwbA==}
engines: {node: '>=10.0.0'} engines: {node: '>=10.0.0'}
dependencies: dependencies:
@ -12920,7 +12926,7 @@ packages:
'@babel/core': 7.17.2 '@babel/core': 7.17.2
'@babel/preset-env': 7.16.11_@babel+core@7.17.2 '@babel/preset-env': 7.16.11_@babel+core@7.17.2
'@babel/runtime': 7.17.2 '@babel/runtime': 7.17.2
'@rollup/plugin-babel': 5.3.0_@babel+core@7.17.2+rollup@2.79.1 '@rollup/plugin-babel': 5.3.0_pf2mys4p2khuj2gysypj3zzjia
'@rollup/plugin-node-resolve': 11.2.1_rollup@2.79.1 '@rollup/plugin-node-resolve': 11.2.1_rollup@2.79.1
'@rollup/plugin-replace': 2.4.2_rollup@2.79.1 '@rollup/plugin-replace': 2.4.2_rollup@2.79.1
'@surma/rollup-plugin-off-main-thread': 2.2.3 '@surma/rollup-plugin-off-main-thread': 2.2.3
@ -12932,7 +12938,7 @@ packages:
lodash: 4.17.21 lodash: 4.17.21
pretty-bytes: 5.6.0 pretty-bytes: 5.6.0
rollup: 2.79.1 rollup: 2.79.1
rollup-plugin-terser: 7.0.2_rollup@2.79.1 rollup-plugin-terser: 7.0.2_acorn@8.8.0+rollup@2.79.1
source-map: 0.8.0-beta.0 source-map: 0.8.0-beta.0
stringify-object: 3.3.0 stringify-object: 3.3.0
strip-comments: 2.0.1 strip-comments: 2.0.1
@ -12955,6 +12961,7 @@ packages:
workbox-window: 6.5.4 workbox-window: 6.5.4
transitivePeerDependencies: transitivePeerDependencies:
- '@types/babel__core' - '@types/babel__core'
- acorn
- supports-color - supports-color
dev: true dev: true
@ -12964,7 +12971,7 @@ packages:
workbox-core: 6.5.4 workbox-core: 6.5.4
dev: true dev: true
/workbox-cli/6.5.4: /workbox-cli/6.5.4_acorn@8.8.0:
resolution: {integrity: sha512-+Cc0jYh25MofhCROZqfQkpYSAGvykyrUVekuuPaLFbJ8qxX/zzX8hRRpglfwxDwokAjz8S20oEph4s+MyQc+Yw==} resolution: {integrity: sha512-+Cc0jYh25MofhCROZqfQkpYSAGvykyrUVekuuPaLFbJ8qxX/zzX8hRRpglfwxDwokAjz8S20oEph4s+MyQc+Yw==}
engines: {node: '>=10.0.0'} engines: {node: '>=10.0.0'}
hasBin: true hasBin: true
@ -12981,9 +12988,10 @@ packages:
stringify-object: 3.3.0 stringify-object: 3.3.0
upath: 1.2.0 upath: 1.2.0
update-notifier: 4.1.3 update-notifier: 4.1.3
workbox-build: 6.5.4 workbox-build: 6.5.4_acorn@8.8.0
transitivePeerDependencies: transitivePeerDependencies:
- '@types/babel__core' - '@types/babel__core'
- acorn
- supports-color - supports-color
dev: true dev: true

View file

@ -4,7 +4,7 @@
<vue-easymde <vue-easymde
:configs="config" :configs="config"
@change="bubble" @change="() => bubble()"
@update:modelValue="handleInput" @update:modelValue="handleInput"
class="content" class="content"
v-if="isEditActive" v-if="isEditActive"
@ -66,32 +66,28 @@
</div> </div>
</template> </template>
<script lang="ts"> <script setup lang="ts">
import {defineComponent} from 'vue' import {computed, nextTick, onMounted, ref, toRefs, watch} from 'vue'
import VueEasymde from './vue-easymde.vue' import VueEasymde from './vue-easymde.vue'
import {marked} from 'marked' import {marked} from 'marked'
import DOMPurify from 'dompurify' import DOMPurify from 'dompurify'
import {setupMarkdownRenderer} from '@/helpers/markdownRenderer'
import {createEasyMDEConfig} from './editorConfig' import {createEasyMDEConfig} from './editorConfig'
import AttachmentModel from '../../models/attachment' import AttachmentModel from '@/models/attachment'
import AttachmentService from '../../services/attachment' import AttachmentService from '@/services/attachment'
import {findCheckboxesInText} from '../../helpers/checklistFromText'
import {setupMarkdownRenderer} from '@/helpers/markdownRenderer'
import {findCheckboxesInText} from '@/helpers/checklistFromText'
import {createRandomID} from '@/helpers/randomId' import {createRandomID} from '@/helpers/randomId'
import BaseButton from '@/components/base/BaseButton.vue' import BaseButton from '@/components/base/BaseButton.vue'
import ButtonLink from '@/components/misc/ButtonLink.vue' import ButtonLink from '@/components/misc/ButtonLink.vue'
import type { IAttachment } from '@/modelTypes/IAttachment'
import type { ITask } from '@/modelTypes/ITask'
export default defineComponent({ const props = defineProps({
name: 'editor',
components: {
VueEasymde,
BaseButton,
ButtonLink,
},
props: {
modelValue: { modelValue: {
type: String, type: String,
default: '', default: '',
@ -135,96 +131,108 @@ export default defineComponent({
type: String, type: String,
default: '', default: '',
}, },
}, })
emits: ['update:modelValue'],
computed: {
showPreviewText() {
return this.isPreviewActive && this.text === '' && this.emptyText !== ''
},
showEditButton() {
return !this.isEditActive && this.text !== ''
},
},
data() {
return {
text: '',
changeTimeout: null,
isEditActive: false,
isPreviewActive: true,
preview: '', const emit = defineEmits(['update:modelValue'])
attachmentService: null,
loadedAttachments: {}, const text = ref('')
config: createEasyMDEConfig({ const changeTimeout = ref<ReturnType<typeof setTimeout> | null>(null)
placeholder: this.placeholder, const isEditActive = ref(false)
uploadImage: this.uploadEnabled, const isPreviewActive = ref(true)
imageUploadFunction: this.uploadCallback,
}), const showPreviewText = computed(() => isPreviewActive.value && text.value === '' && props.emptyText !== '')
checkboxId: createRandomID(), const showEditButton = computed(() => !isEditActive.value && text.value !== '')
}
const preview = ref('')
const attachmentService = new AttachmentService()
type CacheKey = `${ITask['id']}-${IAttachment['id']}`
const loadedAttachments = ref<{[key: CacheKey]: string}>({})
const config = ref(createEasyMDEConfig({
placeholder: props.placeholder,
uploadImage: props.uploadEnabled,
imageUploadFunction: props.uploadCallback,
}))
const checkboxId = ref(createRandomID())
const {modelValue} = toRefs(props)
watch(
modelValue,
async (value) => {
text.value = value
await nextTick()
renderPreview()
}, },
watch: { )
modelValue(modelValue) {
this.text = modelValue watch(
this.$nextTick(this.renderPreview) text,
}, (newVal, oldVal) => {
text(newVal, oldVal) {
// Only bubble the new value if it actually changed, but not if the component just got mounted and the text changed from the outside. // Only bubble the new value if it actually changed, but not if the component just got mounted and the text changed from the outside.
if (oldVal === '' && this.text === this.modelValue) { if (oldVal === '' && text.value === modelValue.value) {
return return
} }
this.bubble() bubble()
}, },
}, )
mounted() {
if (this.modelValue !== '') {
this.text = this.modelValue onMounted(() => {
if (modelValue.value !== '') {
text.value = modelValue.value
} }
if (this.previewIsDefault && this.hasPreview) { if (props.previewIsDefault && props.hasPreview) {
this.$nextTick(this.renderPreview) nextTick(() => renderPreview())
return return
} }
this.isPreviewActive = false isPreviewActive.value = false
this.isEditActive = true isEditActive.value = true
}, })
methods: {
// This gets triggered when only pasting content into the editor. // This gets triggered when only pasting content into the editor.
// A change event would not get generated by that, an input event does. // A change event would not get generated by that, an input event does.
// Therefore, we're using this handler to catch paste events. // Therefore, we're using this handler to catch paste events.
// But because this also gets triggered when typing into the editor, we give // But because this also gets triggered when typing into the editor, we give
// it a higher timeout to make the timouts cancel each other in that case so // it a higher timeout to make the timouts cancel each other in that case so
// that in the end, only one change event is triggered to the outside per change. // that in the end, only one change event is triggered to the outside per change.
handleInput(val) { function handleInput(val: string) {
// Don't bubble if the text is up to date // Don't bubble if the text is up to date
if (val === this.text) { if (val === text.value) {
return return
} }
this.text = val text.value = val
this.bubble(1000) bubble(1000)
},
bubble(timeout = 500) {
if (this.changeTimeout !== null) {
clearTimeout(this.changeTimeout)
} }
this.changeTimeout = setTimeout(() => { function bubble(timeout = 500) {
this.$emit('update:modelValue', this.text) if (changeTimeout.value !== null) {
clearTimeout(changeTimeout.value)
}
changeTimeout.value = setTimeout(() => {
emit('update:modelValue', text.value)
}, timeout) }, timeout)
}, }
replaceAt(str, index, replacement) {
return str.substr(0, index) + replacement + str.substr(index + replacement.length) function replaceAt(str: string, index: number, replacement: string) {
}, return str.slice(0, index) + replacement + str.slice(index + replacement.length)
findNthIndex(str, n) { }
function findNthIndex(str: string, n: number) {
const checkboxes = findCheckboxesInText(str) const checkboxes = findCheckboxesInText(str)
return checkboxes[n] return checkboxes[n]
}, }
renderPreview() {
setupMarkdownRenderer(this.checkboxId)
this.preview = DOMPurify.sanitize(marked(this.text), {ADD_ATTR: ['target']}) function renderPreview() {
setupMarkdownRenderer(checkboxId.value)
preview.value = DOMPurify.sanitize(marked(text.value), {ADD_ATTR: ['target']})
// Since the render function is synchronous, we can't do async http requests in it. // Since the render function is synchronous, we can't do async http requests in it.
// Therefore, we can't resolve the blob url at (markdown) compile time. // Therefore, we can't resolve the blob url at (markdown) compile time.
@ -233,78 +241,70 @@ export default defineComponent({
// dom tree. If we're calling this right after setting this.preview it could be the images were // dom tree. If we're calling this right after setting this.preview it could be the images were
// not already made available. // not already made available.
// Some docs at https://stackoverflow.com/q/62865160/10924593 // Some docs at https://stackoverflow.com/q/62865160/10924593
this.$nextTick(async () => { nextTick().then(async () => {
const attachmentImage = document.getElementsByClassName('attachment-image') const attachmentImage = document.querySelectorAll<HTMLImageElement>('.attachment-image')
if (attachmentImage) { if (attachmentImage) {
for (const img of attachmentImage) { Array.from(attachmentImage).forEach(async (img) => {
// The url is something like /tasks/<id>/attachments/<id> // The url is something like /tasks/<id>/attachments/<id>
const parts = img.dataset.src.substr(window.API_URL.length + 1).split('/') const parts = img.dataset.src?.slice(window.API_URL.length + 1).split('/')
const taskId = parseInt(parts[1]) const taskId = Number(parts[1])
const attachmentId = parseInt(parts[3]) const attachmentId = Number(parts[3])
const cacheKey = `${taskId}-${attachmentId}` const cacheKey: CacheKey = `${taskId}-${attachmentId}`
if (typeof this.loadedAttachments[cacheKey] !== 'undefined') { if (typeof loadedAttachments.value[cacheKey] !== 'undefined') {
img.src = this.loadedAttachments[cacheKey] img.src = loadedAttachments.value[cacheKey]
continue return
} }
const attachment = new AttachmentModel({taskId: taskId, id: attachmentId}) const attachment = new AttachmentModel({taskId: taskId, id: attachmentId})
if (this.attachmentService === null) { const url = await attachmentService.getBlobUrl(attachment)
this.attachmentService = new AttachmentService()
}
const url = await this.attachmentService.getBlobUrl(attachment)
img.src = url img.src = url
this.loadedAttachments[cacheKey] = url loadedAttachments.value[cacheKey] = url
} })
} }
const textCheckbox = document.getElementsByClassName(`text-checkbox-${this.checkboxId}`) const textCheckbox = document.querySelectorAll<HTMLInputElement>(`.text-checkbox-${checkboxId.value}`)
if (textCheckbox) { if (textCheckbox) {
for (const check of textCheckbox) { Array.from(textCheckbox).forEach(check => {
check.removeEventListener('change', this.handleCheckboxClick) check.removeEventListener('change', handleCheckboxClick)
check.addEventListener('change', this.handleCheckboxClick) check.addEventListener('change', handleCheckboxClick)
check.parentElement.classList.add('has-checkbox') check.parentElement?.classList.add('has-checkbox')
} })
} }
}) })
}, }
handleCheckboxClick(e) {
// Find the original markdown checkbox this is targeting
const checked = e.target.checked
const numMarkdownCheck = parseInt(e.target.dataset.checkboxNum)
const index = this.findNthIndex(this.text, numMarkdownCheck) function handleCheckboxClick(e: Event) {
// Find the original markdown checkbox this is targeting
const checked = (e.target as HTMLInputElement).checked
const numMarkdownCheck = Number((e.target as HTMLInputElement).dataset.checkboxNum)
const index = findNthIndex(text.value, numMarkdownCheck)
if (index < 0 || typeof index === 'undefined') { if (index < 0 || typeof index === 'undefined') {
console.debug('no index found') console.debug('no index found')
return return
} }
console.debug(index, this.text.substr(index, 9)) console.debug(index, text.value.slice(index, 9))
const listPrefix = this.text.substr(index, 1) const listPrefix = text.value.slice(index, 1)
if (checked) { text.value = replaceAt(text.value, index, `${listPrefix} ${checked ? '[x]' : '[ ]'} `)
this.text = this.replaceAt(this.text, index, `${listPrefix} [x] `) bubble()
} else { renderPreview()
this.text = this.replaceAt(this.text, index, `${listPrefix} [ ] `)
} }
this.bubble()
this.renderPreview() function toggleEdit() {
}, if (isEditActive.value) {
toggleEdit() { isPreviewActive.value = true
if (this.isEditActive) { isEditActive.value = false
this.isPreviewActive = true renderPreview()
this.isEditActive = false bubble(0) // save instantly
this.renderPreview()
this.bubble(0) // save instantly
} else { } else {
this.isPreviewActive = false isPreviewActive.value = false
this.isEditActive = true isEditActive.value = true
}
} }
},
},
})
</script> </script>
<style lang="scss"> <style lang="scss">

View file

@ -8,33 +8,34 @@ export function setupMarkdownRenderer(checkboxId: string) {
let checkboxNum = -1 let checkboxNum = -1
marked.use({ marked.use({
renderer: { renderer: {
image: (src, title, text) => { image(src: string, title: string, text: string) {
title = title ? ` title="${title}` : '' title = title ? ` title="${title}` : ''
// If the url starts with the api url, the image is likely an attachment and // If the url starts with the api url, the image is likely an attachment and
// we'll need to download and parse it properly. // we'll need to download and parse it properly.
if (src.substr(0, window.API_URL.length + 7) === `${window.API_URL}/tasks/`) { if (src.slice(0, window.API_URL.length + 7) === `${window.API_URL}/tasks/`) {
return `<img data-src="${src}" alt="${text}" ${title} class="attachment-image"/>` return `<img data-src="${src}" alt="${text}" ${title} class="attachment-image"/>`
} }
return `<img src="${src}" alt="${text}" ${title}/>` return `<img src="${src}" alt="${text}" ${title}/>`
}, },
checkbox: (checked) => { checkbox(checked: boolean) {
let checkedString = ''
if (checked) { if (checked) {
checked = ' checked="checked"' checkedString = 'checked'
} }
checkboxNum++ checkboxNum++
return `<input type="checkbox" data-checkbox-num="${checkboxNum}" ${checked} class="text-checkbox-${checkboxId}"/>` return `<input type="checkbox" data-checkbox-num="${checkboxNum}" ${checkedString} class="text-checkbox-${checkboxId}"/>`
}, },
link: (href, title, text) => { link(href: string, title: string, text: string) {
const isLocal = href.startsWith(`${location.protocol}//${location.hostname}`) const isLocal = href.startsWith(`${location.protocol}//${location.hostname}`)
const html = linkRenderer.call(renderer, href, title, text) const html = linkRenderer.call(renderer, href, title, text)
return isLocal ? html : html.replace(/^<a /, '<a target="_blank" rel="noreferrer noopener nofollow" ') return isLocal ? html : html.replace(/^<a /, '<a target="_blank" rel="noreferrer noopener nofollow" ')
}, },
}, },
highlight: function (code, language) { highlight(code, language) {
const validLanguage = hljs.getLanguage(language) ? language : 'plaintext' const validLanguage = hljs.getLanguage(language) ? language : 'plaintext'
return hljs.highlight(code, {language: validLanguage}).value return hljs.highlight(code, {language: validLanguage}).value
}, },

View file

@ -1,5 +1,5 @@
const DEFAULT_ID_LENGTH = 9 const DEFAULT_ID_LENGTH = 9
export function createRandomID(idLength = DEFAULT_ID_LENGTH) { export function createRandomID(idLength = DEFAULT_ID_LENGTH) {
return Math.random().toString(36).substr(2, idLength) return Math.random().toString(36).slice(2, idLength)
} }

View file

@ -285,7 +285,7 @@ const getDateFromWeekday = (text: string): dateFoundResult => {
// matched string comes with a space at the end (last part of the regex). // matched string comes with a space at the end (last part of the regex).
let foundText = results[0] let foundText = results[0]
if (foundText.endsWith(' ')) { if (foundText.endsWith(' ')) {
foundText = foundText.substr(0, foundText.length - 1) foundText = foundText.slice(0, foundText.length - 1)
} }
return { return {

View file

@ -34,8 +34,8 @@ if (apiUrlFromStorage !== null) {
} }
// Make sure the api url does not contain a / at the end // Make sure the api url does not contain a / at the end
if (window.API_URL.substr(window.API_URL.length - 1, window.API_URL.length) === '/') { if (window.API_URL.slice(window.API_URL.length - 1, window.API_URL.length) === '/') {
window.API_URL = window.API_URL.substr(0, window.API_URL.length - 1) window.API_URL = window.API_URL.slice(0, window.API_URL.length - 1)
} }
const app = createApp(App) const app = createApp(App)