]> BookStack Code Mirror - bookstack/commitdiff
Merge branch 'feature/mail-verify-peer' into development
authorDan Brown <redacted>
Sun, 23 Apr 2023 14:05:13 +0000 (15:05 +0100)
committerDan Brown <redacted>
Sun, 23 Apr 2023 14:05:13 +0000 (15:05 +0100)
221 files changed:
.env.example.complete
.github/translators.txt
.github/workflows/lint-js.yml [new file with mode: 0644]
.gitignore
app/Actions/TagRepo.php
app/Auth/Access/EmailConfirmationService.php
app/Auth/Access/UserInviteService.php
app/Auth/Access/UserTokenService.php
app/Http/Controllers/Auth/ConfirmEmailController.php
app/Http/Controllers/RoleController.php
app/Http/Controllers/TagController.php
app/Http/Controllers/UserController.php
composer.lock
dev/build/esbuild.js
dev/docs/development.md
dev/docs/javascript-code.md
dev/docs/javascript-public-events.md [new file with mode: 0644]
lang/ar/settings.php
lang/bg/activities.php
lang/bg/auth.php
lang/bg/common.php
lang/bg/editor.php
lang/bg/errors.php
lang/bg/preferences.php
lang/bg/settings.php
lang/bs/settings.php
lang/ca/settings.php
lang/cs/activities.php
lang/cs/entities.php
lang/cs/settings.php
lang/cy/settings.php
lang/da/settings.php
lang/de/settings.php
lang/de_informal/activities.php
lang/de_informal/entities.php
lang/de_informal/errors.php
lang/de_informal/settings.php
lang/el/activities.php
lang/el/entities.php
lang/el/errors.php
lang/el/settings.php
lang/es/settings.php
lang/es_AR/settings.php
lang/et/settings.php
lang/eu/entities.php
lang/eu/settings.php
lang/fa/activities.php
lang/fa/auth.php
lang/fa/common.php
lang/fa/editor.php
lang/fa/entities.php
lang/fa/preferences.php
lang/fa/settings.php
lang/fr/activities.php
lang/fr/entities.php
lang/fr/settings.php
lang/he/activities.php
lang/he/settings.php
lang/hr/settings.php
lang/hu/settings.php
lang/id/settings.php
lang/it/settings.php
lang/ja/settings.php
lang/ka/settings.php
lang/ko/settings.php
lang/lt/settings.php
lang/lv/activities.php
lang/lv/auth.php
lang/lv/common.php
lang/lv/editor.php
lang/lv/entities.php
lang/lv/errors.php
lang/lv/settings.php
lang/nb/entities.php
lang/nb/errors.php
lang/nb/preferences.php
lang/nb/settings.php
lang/nb/validation.php
lang/nl/activities.php
lang/nl/entities.php
lang/nl/errors.php
lang/nl/settings.php
lang/pl/settings.php
lang/pt/activities.php
lang/pt/entities.php
lang/pt/settings.php
lang/pt_BR/activities.php
lang/pt_BR/errors.php
lang/pt_BR/settings.php
lang/ro/settings.php
lang/ru/settings.php
lang/sk/settings.php
lang/sl/settings.php
lang/sv/settings.php
lang/tr/settings.php
lang/uk/settings.php
lang/uz/settings.php
lang/vi/settings.php
lang/zh_CN/settings.php
lang/zh_TW/settings.php
package-lock.json
package.json
readme.md
resources/js/app.js
resources/js/code.mjs [deleted file]
resources/js/code/index.mjs [new file with mode: 0644]
resources/js/code/languages.js [new file with mode: 0644]
resources/js/code/legacy-modes.mjs [new file with mode: 0644]
resources/js/code/setups.js [new file with mode: 0644]
resources/js/code/simple-editor-interface.js [new file with mode: 0644]
resources/js/code/themes.js [new file with mode: 0644]
resources/js/code/views.js [new file with mode: 0644]
resources/js/components/add-remove-rows.js
resources/js/components/ajax-delete-row.js
resources/js/components/ajax-form.js
resources/js/components/attachments-list.js
resources/js/components/attachments.js
resources/js/components/auto-submit.js
resources/js/components/auto-suggest.js
resources/js/components/back-to-top.js
resources/js/components/book-sort.js
resources/js/components/chapter-contents.js
resources/js/components/code-editor.js
resources/js/components/code-highlighter.js
resources/js/components/code-textarea.js
resources/js/components/collapsible.js
resources/js/components/component.js
resources/js/components/confirm-dialog.js
resources/js/components/custom-checkbox.js
resources/js/components/details-highlighter.js
resources/js/components/dropdown-search.js
resources/js/components/dropdown.js
resources/js/components/dropzone.js
resources/js/components/editor-toolbox.js
resources/js/components/entity-permissions.js
resources/js/components/entity-search.js
resources/js/components/entity-selector-popup.js
resources/js/components/entity-selector.js
resources/js/components/event-emit-select.js
resources/js/components/expand-toggle.js
resources/js/components/global-search.js
resources/js/components/header-mobile-toggle.js
resources/js/components/image-manager.js
resources/js/components/image-picker.js
resources/js/components/index.js
resources/js/components/list-sort-control.js
resources/js/components/markdown-editor.js
resources/js/components/new-user-password.js
resources/js/components/notification.js
resources/js/components/optional-input.js
resources/js/components/page-comments.js
resources/js/components/page-display.js
resources/js/components/page-editor.js
resources/js/components/page-picker.js
resources/js/components/permissions-table.js
resources/js/components/pointer.js
resources/js/components/popup.js
resources/js/components/setting-app-color-scheme.js
resources/js/components/setting-color-picker.js
resources/js/components/setting-homepage-control.js
resources/js/components/shelf-sort.js
resources/js/components/shortcut-input.js
resources/js/components/shortcuts.js
resources/js/components/sortable-list.js
resources/js/components/submit-on-change.js
resources/js/components/tabs.js
resources/js/components/tag-manager.js
resources/js/components/template-manager.js
resources/js/components/toggle-switch.js
resources/js/components/tri-layout.js
resources/js/components/user-select.js
resources/js/components/webhook-events.js
resources/js/components/wysiwyg-editor.js
resources/js/markdown/actions.js
resources/js/markdown/codemirror.js
resources/js/markdown/common-events.js
resources/js/markdown/display.js
resources/js/markdown/editor.js
resources/js/markdown/markdown.js
resources/js/markdown/settings.js
resources/js/markdown/shortcuts.js
resources/js/services/animations.js
resources/js/services/clipboard.js
resources/js/services/components.js
resources/js/services/dates.js
resources/js/services/dom.js
resources/js/services/drawio.js
resources/js/services/events.js
resources/js/services/http.js
resources/js/services/keyboard-navigation.js
resources/js/services/text.js
resources/js/services/translations.js
resources/js/services/util.js
resources/js/services/vdom.js
resources/js/wysiwyg/common-events.js
resources/js/wysiwyg/config.js
resources/js/wysiwyg/drop-paste-handling.js
resources/js/wysiwyg/icons.js
resources/js/wysiwyg/plugin-codeeditor.js
resources/js/wysiwyg/plugin-drawio.js
resources/js/wysiwyg/plugins-about.js
resources/js/wysiwyg/plugins-customhr.js
resources/js/wysiwyg/plugins-details.js
resources/js/wysiwyg/plugins-imagemanager.js
resources/js/wysiwyg/plugins-stub.js
resources/js/wysiwyg/plugins-tasklist.js
resources/js/wysiwyg/scrolling.js
resources/js/wysiwyg/shortcuts.js
resources/js/wysiwyg/toolbars.js
resources/js/wysiwyg/util.js
resources/sass/_codemirror.scss
resources/sass/_components.scss
resources/sass/_forms.scss
resources/sass/_pages.scss
resources/sass/_text.scss
resources/views/pages/parts/code-editor.blade.php
resources/views/pages/parts/markdown-editor.blade.php
resources/views/pages/parts/pointer.blade.php
resources/views/users/delete.blade.php
tests/Permissions/RolesTest.php
tests/User/UserManagementTest.php

index 9ce33f58f82b77900eb7ec20bb27e1fdae315f3d..7071846a36e9da31868d3b7a1ff24096463dd3df 100644 (file)
@@ -3,6 +3,10 @@
 # Each option is shown with it's default value.
 # Do not copy this whole file to use as your '.env' file.
 
+# The details here only serve as a quick reference.
+# Please refer to the BookStack documentation for full details:
+# https://www.bookstackapp.com/docs/
+
 # Application environment
 # Can be 'production', 'development', 'testing' or 'demo'
 APP_ENV=production
@@ -323,6 +327,13 @@ FILE_UPLOAD_SIZE_LIMIT=50
 # Can be 'a4' or 'letter'.
 EXPORT_PAGE_SIZE=a4
 
+# Set path to wkhtmltopdf binary for PDF generation.
+# Can be 'false' or a path path like: '/home/bins/wkhtmltopdf'
+# When false, BookStack will attempt to find a wkhtmltopdf in the application
+# root folder then fall back to the default dompdf renderer if no binary exists.
+# Only used if 'ALLOW_UNTRUSTED_SERVER_FETCHING=true' which disables security protections.
+WKHTMLTOPDF=false
+
 # Allow <script> tags in page content
 # Note, if set to 'true' the page editor may still escape scripts.
 ALLOW_CONTENT_SCRIPTS=false
index 5d29752a83be2cdfd2790661bde2ce3ef8380894..a9f8d73d0258eea46ad2e8dc8cdb183c1b19d7de 100644 (file)
@@ -311,3 +311,12 @@ m4tthi4s :: French
 toras9000 :: Japanese
 pathab :: German
 MichelSchoon85 :: Dutch
+Jøran Haugli (haugli92) :: Norwegian Bokmal
+Vasileios Kouvelis (VasilisKouvelis) :: Greek
+Dremski :: Bulgarian
+Frédéric SENE (nothingfr) :: French
+bendem :: French
+kostasdizas :: Greek
+Ricardo Schroeder (brownstone666) :: Portuguese, Brazilian
+Eitan MG (EitanMG) :: Hebrew
+Robin Flikkema (RobinFlikkema) :: Dutch
diff --git a/.github/workflows/lint-js.yml b/.github/workflows/lint-js.yml
new file mode 100644 (file)
index 0000000..a8bd8ab
--- /dev/null
@@ -0,0 +1,16 @@
+name: lint-js
+
+on: [push, pull_request]
+
+jobs:
+  build:
+    if: ${{ github.ref != 'refs/heads/l10n_development' }}
+    runs-on: ubuntu-22.04
+    steps:
+    - uses: actions/checkout@v1
+
+    - name: Install NPM deps
+      run: npm ci
+
+    - name: Run formatting check
+      run: npm run lint
index 90b80e7b85df2cc6ca596a2f5e0c41f928a5bd61..5f3aa6600066ce2dcd5e702153da2a8f856ae418 100644 (file)
@@ -21,8 +21,10 @@ yarn.lock
 nbproject
 .buildpath
 .project
+.nvmrc
 .settings/
 webpack-stats.json
 .phpunit.result.cache
 .DS_Store
-phpstan.neon
\ No newline at end of file
+phpstan.neon
+esbuild-meta.json
\ No newline at end of file
index cece30de003b3a48b0d72f2d0e30b28e7dfa828e..13d1d957e962f21237e80451fdcffe7cfa448175 100644 (file)
@@ -11,11 +11,9 @@ use Illuminate\Support\Facades\DB;
 
 class TagRepo
 {
-    protected PermissionApplicator $permissions;
-
-    public function __construct(PermissionApplicator $permissions)
-    {
-        $this->permissions = $permissions;
+    public function __construct(
+        protected PermissionApplicator $permissions
+    ) {
     }
 
     /**
@@ -90,6 +88,7 @@ class TagRepo
     {
         $query = Tag::query()
             ->select('*', DB::raw('count(*) as count'))
+            ->where('value', '!=', '')
             ->groupBy('value');
 
         if ($searchTerm) {
index 9c357d95f955f8dfde8227bd3c4525fd056d5d21..1873cad086ca7fa7f762425f18d9b4a0967a8dfe 100644 (file)
@@ -8,8 +8,8 @@ use BookStack\Notifications\ConfirmEmail;
 
 class EmailConfirmationService extends UserTokenService
 {
-    protected $tokenTable = 'email_confirmations';
-    protected $expiryTime = 24;
+    protected string $tokenTable = 'email_confirmations';
+    protected int $expiryTime = 24;
 
     /**
      * Create new confirmation for a user,
index d884cd6369317ac5cd832e6dff63ea6636a6cbf9..191a03dd5f1d0502372cfd911a6f25a7f4945f28 100644 (file)
@@ -7,14 +7,12 @@ use BookStack\Notifications\UserInvite;
 
 class UserInviteService extends UserTokenService
 {
-    protected $tokenTable = 'user_invites';
-    protected $expiryTime = 336; // Two weeks
+    protected string $tokenTable = 'user_invites';
+    protected int $expiryTime = 336; // Two weeks
 
     /**
      * Send an invitation to a user to sign into BookStack
      * Removes existing invitation tokens.
-     *
-     * @param User $user
      */
     public function sendInvitation(User $user)
     {
index ffd828ab5095194b8df7ab638a73980215095c52..8dfe570f99208d26a05db2942a4b6af1d3edff76 100644 (file)
@@ -14,41 +14,29 @@ class UserTokenService
 {
     /**
      * Name of table where user tokens are stored.
-     *
-     * @var string
      */
-    protected $tokenTable = 'user_tokens';
+    protected string $tokenTable = 'user_tokens';
 
     /**
      * Token expiry time in hours.
-     *
-     * @var int
      */
-    protected $expiryTime = 24;
+    protected int $expiryTime = 24;
 
     /**
-     * Delete all email confirmations that belong to a user.
-     *
-     * @param User $user
-     *
-     * @return mixed
+     * Delete all tokens that belong to a user.
      */
-    public function deleteByUser(User $user)
+    public function deleteByUser(User $user): void
     {
-        return DB::table($this->tokenTable)
+        DB::table($this->tokenTable)
             ->where('user_id', '=', $user->id)
             ->delete();
     }
 
     /**
-     * Get the user id from a token, while check the token exists and has not expired.
-     *
-     * @param string $token
+     * Get the user id from a token, while checking the token exists and has not expired.
      *
      * @throws UserTokenNotFoundException
      * @throws UserTokenExpiredException
-     *
-     * @return int
      */
     public function checkTokenAndGetUserId(string $token): int
     {
@@ -67,8 +55,6 @@ class UserTokenService
 
     /**
      * Creates a unique token within the email confirmation database.
-     *
-     * @return string
      */
     protected function generateToken(): string
     {
@@ -82,10 +68,6 @@ class UserTokenService
 
     /**
      * Generate and store a token for the given user.
-     *
-     * @param User $user
-     *
-     * @return string
      */
     protected function createTokenForUser(User $user): string
     {
@@ -102,10 +84,6 @@ class UserTokenService
 
     /**
      * Check if the given token exists.
-     *
-     * @param string $token
-     *
-     * @return bool
      */
     protected function tokenExists(string $token): bool
     {
@@ -115,12 +93,8 @@ class UserTokenService
 
     /**
      * Get a token entry for the given token.
-     *
-     * @param string $token
-     *
-     * @return object|null
      */
-    protected function getEntryByToken(string $token)
+    protected function getEntryByToken(string $token): ?stdClass
     {
         return DB::table($this->tokenTable)
             ->where('token', '=', $token)
@@ -129,10 +103,6 @@ class UserTokenService
 
     /**
      * Check if the given token entry has expired.
-     *
-     * @param stdClass $tokenEntry
-     *
-     * @return bool
      */
     protected function entryExpired(stdClass $tokenEntry): bool
     {
index b282d0601f31eaab351f971be08d0f0ad6540150..fdde8e70c1b9e851cf79fac5261b72b7fa25a723 100644 (file)
@@ -14,21 +14,11 @@ use Illuminate\Http\Request;
 
 class ConfirmEmailController extends Controller
 {
-    protected EmailConfirmationService $emailConfirmationService;
-    protected LoginService $loginService;
-    protected UserRepo $userRepo;
-
-    /**
-     * Create a new controller instance.
-     */
     public function __construct(
-        EmailConfirmationService $emailConfirmationService,
-        LoginService $loginService,
-        UserRepo $userRepo
+        protected EmailConfirmationService $emailConfirmationService,
+        protected LoginService $loginService,
+        protected UserRepo $userRepo
     ) {
-        $this->emailConfirmationService = $emailConfirmationService;
-        $this->loginService = $loginService;
-        $this->userRepo = $userRepo;
     }
 
     /**
index 135ba329f61cfeb6ea0fd11705a5d1be90ddde82..6d397bdae61c491f30e183f7c4efd23ecf2c5ca8 100644 (file)
@@ -151,7 +151,8 @@ class RoleController extends Controller
         $this->checkPermission('user-roles-manage');
 
         try {
-            $this->permissionsRepo->deleteRole($id, $request->get('migrate_role_id', 0));
+            $migrateRoleId = intval($request->get('migrate_role_id') ?: "0");
+            $this->permissionsRepo->deleteRole($id, $migrateRoleId);
         } catch (PermissionsException $e) {
             $this->showErrorNotification($e->getMessage());
 
index 6c2876043c4fa78e24c959bf1ca00442183d5b58..00aaf2b7899f775b9db43915b2b50659f7f3ba93 100644 (file)
@@ -8,11 +8,9 @@ use Illuminate\Http\Request;
 
 class TagController extends Controller
 {
-    protected TagRepo $tagRepo;
-
-    public function __construct(TagRepo $tagRepo)
-    {
-        $this->tagRepo = $tagRepo;
+    public function __construct(
+        protected TagRepo $tagRepo
+    ) {
     }
 
     /**
index 2fcfa4289e20ec31f47f1aa5dd52f8e461d73508..cd95f7220766fb6598567afe8ca9d2d7098ac331 100644 (file)
@@ -197,7 +197,7 @@ class UserController extends Controller
         $this->checkPermissionOrCurrentUser('users-manage', $id);
 
         $user = $this->userRepo->getById($id);
-        $newOwnerId = $request->get('new_owner_id', null);
+        $newOwnerId = intval($request->get('new_owner_id')) ?: null;
 
         $this->userRepo->destroy($user, $newOwnerId);
 
index bbdf4efa9b0fe7b0110fd070ba0a2f63d25a38d9..d4f200d0d5004fbd1d958213d4abb27e8edbfab4 100644 (file)
@@ -8,23 +8,27 @@
     "packages": [
         {
             "name": "aws/aws-crt-php",
-            "version": "v1.0.4",
+            "version": "v1.2.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/awslabs/aws-crt-php.git",
-                "reference": "f5c64ee7c5fce196e2519b3d9b7138649efe032d"
+                "reference": "1926277fc71d253dfa820271ac5987bdb193ccf5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/f5c64ee7c5fce196e2519b3d9b7138649efe032d",
-                "reference": "f5c64ee7c5fce196e2519b3d9b7138649efe032d",
+                "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/1926277fc71d253dfa820271ac5987bdb193ccf5",
+                "reference": "1926277fc71d253dfa820271ac5987bdb193ccf5",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.5"
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.8.35|^5.6.3"
+                "phpunit/phpunit": "^4.8.35||^5.6.3||^9.5",
+                "yoast/phpunit-polyfills": "^1.0"
+            },
+            "suggest": {
+                "ext-awscrt": "Make sure you install awscrt native extension to use any of the functionality."
             },
             "type": "library",
             "autoload": {
@@ -43,7 +47,7 @@
                 }
             ],
             "description": "AWS Common Runtime for PHP",
-            "homepage": "http://aws.amazon.com/sdkforphp",
+            "homepage": "https://github.com/awslabs/aws-crt-php",
             "keywords": [
                 "amazon",
                 "aws",
             ],
             "support": {
                 "issues": "https://github.com/awslabs/aws-crt-php/issues",
-                "source": "https://github.com/awslabs/aws-crt-php/tree/v1.0.4"
+                "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.1"
             },
-            "time": "2023-01-31T23:08:25+00:00"
+            "time": "2023-03-24T20:22:19+00:00"
         },
         {
             "name": "aws/aws-sdk-php",
-            "version": "3.261.10",
+            "version": "3.263.14",
             "source": {
                 "type": "git",
                 "url": "https://github.com/aws/aws-sdk-php.git",
-                "reference": "4889eff2b3fe35e878fbcaf8374d73f043609170"
+                "reference": "7a6a43fad8899e3be3c46471fa3802331620e36b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/4889eff2b3fe35e878fbcaf8374d73f043609170",
-                "reference": "4889eff2b3fe35e878fbcaf8374d73f043609170",
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/7a6a43fad8899e3be3c46471fa3802331620e36b",
+                "reference": "7a6a43fad8899e3be3c46471fa3802331620e36b",
                 "shasum": ""
             },
             "require": {
@@ -77,7 +81,7 @@
                 "ext-simplexml": "*",
                 "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5",
                 "guzzlehttp/promises": "^1.4.0",
-                "guzzlehttp/psr7": "^1.8.5 || ^2.3",
+                "guzzlehttp/psr7": "^1.9.1 || ^2.4.5",
                 "mtdowling/jmespath.php": "^2.6",
                 "php": ">=5.5"
             },
                 "paragonie/random_compat": ">= 2",
                 "phpunit/phpunit": "^4.8.35 || ^5.6.3 || ^9.5",
                 "psr/cache": "^1.0",
+                "psr/http-message": "^1.0",
                 "psr/simple-cache": "^1.0",
                 "sebastian/comparator": "^1.2.3 || ^4.0",
                 "yoast/phpunit-polyfills": "^1.0"
             "support": {
                 "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
-                "source": "https://github.com/aws/aws-sdk-php/tree/3.261.10"
+                "source": "https://github.com/aws/aws-sdk-php/tree/3.263.14"
             },
-            "time": "2023-03-13T18:19:14+00:00"
+            "time": "2023-04-20T18:21:44+00:00"
         },
         {
             "name": "bacon/bacon-qr-code",
         },
         {
             "name": "brick/math",
-            "version": "0.10.2",
+            "version": "0.11.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/brick/math.git",
-                "reference": "459f2781e1a08d52ee56b0b1444086e038561e3f"
+                "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/brick/math/zipball/459f2781e1a08d52ee56b0b1444086e038561e3f",
-                "reference": "459f2781e1a08d52ee56b0b1444086e038561e3f",
+                "url": "https://api.github.com/repos/brick/math/zipball/0ad82ce168c82ba30d1c01ec86116ab52f589478",
+                "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478",
                 "shasum": ""
             },
             "require": {
-                "ext-json": "*",
-                "php": "^7.4 || ^8.0"
+                "php": "^8.0"
             },
             "require-dev": {
                 "php-coveralls/php-coveralls": "^2.2",
                 "phpunit/phpunit": "^9.0",
-                "vimeo/psalm": "4.25.0"
+                "vimeo/psalm": "5.0.0"
             },
             "type": "library",
             "autoload": {
             ],
             "support": {
                 "issues": "https://github.com/brick/math/issues",
-                "source": "https://github.com/brick/math/tree/0.10.2"
+                "source": "https://github.com/brick/math/tree/0.11.0"
             },
             "funding": [
                 {
                     "type": "github"
                 }
             ],
-            "time": "2022-08-10T22:54:19+00:00"
+            "time": "2023-01-15T23:15:59+00:00"
         },
         {
             "name": "dasprid/enum",
         },
         {
             "name": "doctrine/dbal",
-            "version": "3.6.1",
+            "version": "3.6.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/dbal.git",
-                "reference": "57815c7bbcda3cd18871d253c1dd8cbe56f8526e"
+                "reference": "b4bd1cfbd2b916951696d82e57d054394d84864c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/dbal/zipball/57815c7bbcda3cd18871d253c1dd8cbe56f8526e",
-                "reference": "57815c7bbcda3cd18871d253c1dd8cbe56f8526e",
+                "url": "https://api.github.com/repos/doctrine/dbal/zipball/b4bd1cfbd2b916951696d82e57d054394d84864c",
+                "reference": "b4bd1cfbd2b916951696d82e57d054394d84864c",
                 "shasum": ""
             },
             "require": {
                 "doctrine/coding-standard": "11.1.0",
                 "fig/log-test": "^1",
                 "jetbrains/phpstorm-stubs": "2022.3",
-                "phpstan/phpstan": "1.10.3",
+                "phpstan/phpstan": "1.10.9",
                 "phpstan/phpstan-strict-rules": "^1.5",
-                "phpunit/phpunit": "9.6.4",
+                "phpunit/phpunit": "9.6.6",
                 "psalm/plugin-phpunit": "0.18.4",
                 "squizlabs/php_codesniffer": "3.7.2",
                 "symfony/cache": "^5.4|^6.0",
             ],
             "support": {
                 "issues": "https://github.com/doctrine/dbal/issues",
-                "source": "https://github.com/doctrine/dbal/tree/3.6.1"
+                "source": "https://github.com/doctrine/dbal/tree/3.6.2"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-03-02T19:26:24+00:00"
+            "time": "2023-04-14T07:25:38+00:00"
         },
         {
             "name": "doctrine/deprecations",
         },
         {
             "name": "guzzlehttp/guzzle",
-            "version": "7.5.0",
+            "version": "7.5.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/guzzle.git",
-                "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba"
+                "reference": "b964ca597e86b752cd994f27293e9fa6b6a95ed9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba",
-                "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b964ca597e86b752cd994f27293e9fa6b6a95ed9",
+                "reference": "b964ca597e86b752cd994f27293e9fa6b6a95ed9",
                 "shasum": ""
             },
             "require": {
                 "ext-json": "*",
                 "guzzlehttp/promises": "^1.5",
-                "guzzlehttp/psr7": "^1.9 || ^2.4",
+                "guzzlehttp/psr7": "^1.9.1 || ^2.4.5",
                 "php": "^7.2.5 || ^8.0",
                 "psr/http-client": "^1.0",
                 "symfony/deprecation-contracts": "^2.2 || ^3.0"
             ],
             "support": {
                 "issues": "https://github.com/guzzle/guzzle/issues",
-                "source": "https://github.com/guzzle/guzzle/tree/7.5.0"
+                "source": "https://github.com/guzzle/guzzle/tree/7.5.1"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-08-28T15:39:27+00:00"
+            "time": "2023-04-17T16:30:08+00:00"
         },
         {
             "name": "guzzlehttp/promises",
         },
         {
             "name": "guzzlehttp/psr7",
-            "version": "2.4.4",
+            "version": "2.5.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/psr7.git",
-                "reference": "3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf"
+                "reference": "b635f279edd83fc275f822a1188157ffea568ff6"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/psr7/zipball/3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf",
-                "reference": "3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/b635f279edd83fc275f822a1188157ffea568ff6",
+                "reference": "b635f279edd83fc275f822a1188157ffea568ff6",
                 "shasum": ""
             },
             "require": {
                 "php": "^7.2.5 || ^8.0",
                 "psr/http-factory": "^1.0",
-                "psr/http-message": "^1.0",
+                "psr/http-message": "^1.1 || ^2.0",
                 "ralouphie/getallheaders": "^3.0"
             },
             "provide": {
                 "bamarni-bin": {
                     "bin-links": true,
                     "forward-command": false
-                },
-                "branch-alias": {
-                    "dev-master": "2.4-dev"
                 }
             },
             "autoload": {
             ],
             "support": {
                 "issues": "https://github.com/guzzle/psr7/issues",
-                "source": "https://github.com/guzzle/psr7/tree/2.4.4"
+                "source": "https://github.com/guzzle/psr7/tree/2.5.0"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-03-09T13:19:02+00:00"
+            "time": "2023-04-17T16:11:26+00:00"
         },
         {
             "name": "guzzlehttp/uri-template",
         },
         {
             "name": "knplabs/knp-snappy",
-            "version": "v1.4.1",
+            "version": "v1.4.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/KnpLabs/snappy.git",
-                "reference": "5126fb5b335ec929a226314d40cd8dad497c3d67"
+                "reference": "b66f79334421c26d9c244427963fa2d92980b5d3"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/KnpLabs/snappy/zipball/5126fb5b335ec929a226314d40cd8dad497c3d67",
-                "reference": "5126fb5b335ec929a226314d40cd8dad497c3d67",
+                "url": "https://api.github.com/repos/KnpLabs/snappy/zipball/b66f79334421c26d9c244427963fa2d92980b5d3",
+                "reference": "b66f79334421c26d9c244427963fa2d92980b5d3",
                 "shasum": ""
             },
             "require": {
             ],
             "support": {
                 "issues": "https://github.com/KnpLabs/snappy/issues",
-                "source": "https://github.com/KnpLabs/snappy/tree/v1.4.1"
+                "source": "https://github.com/KnpLabs/snappy/tree/v1.4.2"
             },
-            "time": "2022-01-07T13:03:38+00:00"
+            "time": "2023-03-17T14:47:54+00:00"
         },
         {
             "name": "laravel/framework",
-            "version": "v9.52.4",
+            "version": "v9.52.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/laravel/framework.git",
-                "reference": "9239128cfb4d22afefb64060dfecf53e82987267"
+                "reference": "16454f17a2585c4589f721655fc5133270aadf8c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/laravel/framework/zipball/9239128cfb4d22afefb64060dfecf53e82987267",
-                "reference": "9239128cfb4d22afefb64060dfecf53e82987267",
+                "url": "https://api.github.com/repos/laravel/framework/zipball/16454f17a2585c4589f721655fc5133270aadf8c",
+                "reference": "16454f17a2585c4589f721655fc5133270aadf8c",
                 "shasum": ""
             },
             "require": {
                 "league/flysystem-read-only": "^3.3",
                 "league/flysystem-sftp-v3": "^3.0",
                 "mockery/mockery": "^1.5.1",
-                "orchestra/testbench-core": "^7.16",
+                "orchestra/testbench-core": "^7.24",
                 "pda/pheanstalk": "^4.0",
                 "phpstan/phpdoc-parser": "^1.15",
                 "phpstan/phpstan": "^1.4.7",
                 "issues": "https://github.com/laravel/framework/issues",
                 "source": "https://github.com/laravel/framework"
             },
-            "time": "2023-02-22T14:38:06+00:00"
+            "time": "2023-04-18T13:44:55+00:00"
         },
         {
             "name": "laravel/serializable-closure",
         },
         {
             "name": "league/commonmark",
-            "version": "2.3.9",
+            "version": "2.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/thephpleague/commonmark.git",
-                "reference": "c1e114f74e518daca2729ea8c4bf1167038fa4b5"
+                "reference": "d44a24690f16b8c1808bf13b1bd54ae4c63ea048"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/c1e114f74e518daca2729ea8c4bf1167038fa4b5",
-                "reference": "c1e114f74e518daca2729ea8c4bf1167038fa4b5",
+                "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/d44a24690f16b8c1808bf13b1bd54ae4c63ea048",
+                "reference": "d44a24690f16b8c1808bf13b1bd54ae4c63ea048",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "2.4-dev"
+                    "dev-main": "2.5-dev"
                 }
             },
             "autoload": {
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-02-15T14:07:24+00:00"
+            "time": "2023-03-24T15:16:10+00:00"
         },
         {
             "name": "league/config",
         },
         {
             "name": "league/flysystem",
-            "version": "3.12.3",
+            "version": "3.14.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/thephpleague/flysystem.git",
-                "reference": "81e87e74dd5213795c7846d65089712d2dda90ce"
+                "reference": "e2a279d7f47d9098e479e8b21f7fb8b8de230158"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/81e87e74dd5213795c7846d65089712d2dda90ce",
-                "reference": "81e87e74dd5213795c7846d65089712d2dda90ce",
+                "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/e2a279d7f47d9098e479e8b21f7fb8b8de230158",
+                "reference": "e2a279d7f47d9098e479e8b21f7fb8b8de230158",
                 "shasum": ""
             },
             "require": {
             ],
             "support": {
                 "issues": "https://github.com/thephpleague/flysystem/issues",
-                "source": "https://github.com/thephpleague/flysystem/tree/3.12.3"
+                "source": "https://github.com/thephpleague/flysystem/tree/3.14.0"
             },
             "funding": [
                 {
                 {
                     "url": "https://github.com/frankdejonge",
                     "type": "github"
-                },
-                {
-                    "url": "https://tidelift.com/funding/github/packagist/league/flysystem",
-                    "type": "tidelift"
                 }
             ],
-            "time": "2023-02-18T15:32:41+00:00"
+            "time": "2023-04-11T18:11:47+00:00"
         },
         {
             "name": "league/flysystem-aws-s3-v3",
-            "version": "3.12.2",
+            "version": "3.13.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git",
-                "reference": "645e14e4a80bd2da8b01e57388e7296a695a80c2"
+                "reference": "8e04cbb403d4dfd5b73a2f8685f1df395bd177eb"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/645e14e4a80bd2da8b01e57388e7296a695a80c2",
-                "reference": "645e14e4a80bd2da8b01e57388e7296a695a80c2",
+                "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/8e04cbb403d4dfd5b73a2f8685f1df395bd177eb",
+                "reference": "8e04cbb403d4dfd5b73a2f8685f1df395bd177eb",
                 "shasum": ""
             },
             "require": {
             ],
             "support": {
                 "issues": "https://github.com/thephpleague/flysystem-aws-s3-v3/issues",
-                "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.12.2"
+                "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.13.0"
             },
             "funding": [
                 {
                 {
                     "url": "https://github.com/frankdejonge",
                     "type": "github"
-                },
-                {
-                    "url": "https://tidelift.com/funding/github/packagist/league/flysystem",
-                    "type": "tidelift"
                 }
             ],
-            "time": "2023-01-17T14:15:08+00:00"
+            "time": "2023-03-16T14:29:01+00:00"
         },
         {
             "name": "league/html-to-markdown",
         },
         {
             "name": "league/oauth2-client",
-            "version": "2.6.1",
+            "version": "2.7.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/thephpleague/oauth2-client.git",
-                "reference": "2334c249907190c132364f5dae0287ab8666aa19"
+                "reference": "160d6274b03562ebeb55ed18399281d8118b76c8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/2334c249907190c132364f5dae0287ab8666aa19",
-                "reference": "2334c249907190c132364f5dae0287ab8666aa19",
+                "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/160d6274b03562ebeb55ed18399281d8118b76c8",
+                "reference": "160d6274b03562ebeb55ed18399281d8118b76c8",
                 "shasum": ""
             },
             "require": {
             ],
             "support": {
                 "issues": "https://github.com/thephpleague/oauth2-client/issues",
-                "source": "https://github.com/thephpleague/oauth2-client/tree/2.6.1"
+                "source": "https://github.com/thephpleague/oauth2-client/tree/2.7.0"
             },
-            "time": "2021-12-22T16:42:49+00:00"
+            "time": "2023-04-16T18:19:15+00:00"
         },
         {
             "name": "masterminds/html5",
         },
         {
             "name": "psr/http-client",
-            "version": "1.0.1",
+            "version": "1.0.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/http-client.git",
-                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
+                "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
-                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+                "url": "https://api.github.com/repos/php-fig/http-client/zipball/0955afe48220520692d2d09f7ab7e0f93ffd6a31",
+                "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31",
                 "shasum": ""
             },
             "require": {
                 "php": "^7.0 || ^8.0",
-                "psr/http-message": "^1.0"
+                "psr/http-message": "^1.0 || ^2.0"
             },
             "type": "library",
             "extra": {
             "authors": [
                 {
                     "name": "PHP-FIG",
-                    "homepage": "http://www.php-fig.org/"
+                    "homepage": "https://www.php-fig.org/"
                 }
             ],
             "description": "Common interface for HTTP clients",
                 "psr-18"
             ],
             "support": {
-                "source": "https://github.com/php-fig/http-client/tree/master"
+                "source": "https://github.com/php-fig/http-client/tree/1.0.2"
             },
-            "time": "2020-06-29T06:28:15+00:00"
+            "time": "2023-04-10T20:12:12+00:00"
         },
         {
             "name": "psr/http-factory",
-            "version": "1.0.1",
+            "version": "1.0.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/http-factory.git",
-                "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be"
+                "reference": "e616d01114759c4c489f93b099585439f795fe35"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
-                "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
+                "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35",
+                "reference": "e616d01114759c4c489f93b099585439f795fe35",
                 "shasum": ""
             },
             "require": {
                 "php": ">=7.0.0",
-                "psr/http-message": "^1.0"
+                "psr/http-message": "^1.0 || ^2.0"
             },
             "type": "library",
             "extra": {
             "authors": [
                 {
                     "name": "PHP-FIG",
-                    "homepage": "http://www.php-fig.org/"
+                    "homepage": "https://www.php-fig.org/"
                 }
             ],
             "description": "Common interfaces for PSR-7 HTTP message factories",
                 "response"
             ],
             "support": {
-                "source": "https://github.com/php-fig/http-factory/tree/master"
+                "source": "https://github.com/php-fig/http-factory/tree/1.0.2"
             },
-            "time": "2019-04-30T12:38:16+00:00"
+            "time": "2023-04-10T20:10:41+00:00"
         },
         {
             "name": "psr/http-message",
-            "version": "1.0.1",
+            "version": "2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/http-message.git",
-                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+                "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
-                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+                "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.0"
+                "php": "^7.2 || ^8.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.0.x-dev"
+                    "dev-master": "2.0.x-dev"
                 }
             },
             "autoload": {
             "authors": [
                 {
                     "name": "PHP-FIG",
-                    "homepage": "http://www.php-fig.org/"
+                    "homepage": "https://www.php-fig.org/"
                 }
             ],
             "description": "Common interface for HTTP messages",
                 "response"
             ],
             "support": {
-                "source": "https://github.com/php-fig/http-message/tree/master"
+                "source": "https://github.com/php-fig/http-message/tree/2.0"
             },
-            "time": "2016-08-06T14:39:51+00:00"
+            "time": "2023-04-04T09:54:51+00:00"
         },
         {
             "name": "psr/log",
         },
         {
             "name": "psy/psysh",
-            "version": "v0.11.12",
+            "version": "v0.11.15",
             "source": {
                 "type": "git",
                 "url": "https://github.com/bobthecow/psysh.git",
-                "reference": "52cb7c47d403c31c0adc9bf7710fc355f93c20f7"
+                "reference": "5350ce0ec8ecf2c5b5cf554cd2496f97b444af85"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/bobthecow/psysh/zipball/52cb7c47d403c31c0adc9bf7710fc355f93c20f7",
-                "reference": "52cb7c47d403c31c0adc9bf7710fc355f93c20f7",
+                "url": "https://api.github.com/repos/bobthecow/psysh/zipball/5350ce0ec8ecf2c5b5cf554cd2496f97b444af85",
+                "reference": "5350ce0ec8ecf2c5b5cf554cd2496f97b444af85",
                 "shasum": ""
             },
             "require": {
             ],
             "support": {
                 "issues": "https://github.com/bobthecow/psysh/issues",
-                "source": "https://github.com/bobthecow/psysh/tree/v0.11.12"
+                "source": "https://github.com/bobthecow/psysh/tree/v0.11.15"
             },
-            "time": "2023-01-29T21:24:40+00:00"
+            "time": "2023-04-07T21:57:09+00:00"
         },
         {
             "name": "ralouphie/getallheaders",
         },
         {
             "name": "ramsey/uuid",
-            "version": "4.7.3",
+            "version": "4.7.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/ramsey/uuid.git",
-                "reference": "433b2014e3979047db08a17a205f410ba3869cf2"
+                "reference": "60a4c63ab724854332900504274f6150ff26d286"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/ramsey/uuid/zipball/433b2014e3979047db08a17a205f410ba3869cf2",
-                "reference": "433b2014e3979047db08a17a205f410ba3869cf2",
+                "url": "https://api.github.com/repos/ramsey/uuid/zipball/60a4c63ab724854332900504274f6150ff26d286",
+                "reference": "60a4c63ab724854332900504274f6150ff26d286",
                 "shasum": ""
             },
             "require": {
-                "brick/math": "^0.8.8 || ^0.9 || ^0.10",
+                "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11",
                 "ext-json": "*",
                 "php": "^8.0",
                 "ramsey/collection": "^1.2 || ^2.0"
             ],
             "support": {
                 "issues": "https://github.com/ramsey/uuid/issues",
-                "source": "https://github.com/ramsey/uuid/tree/4.7.3"
+                "source": "https://github.com/ramsey/uuid/tree/4.7.4"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-01-12T18:13:24+00:00"
+            "time": "2023-04-15T23:01:58+00:00"
         },
         {
             "name": "robrichards/xmlseclibs",
         },
         {
             "name": "filp/whoops",
-            "version": "2.15.1",
+            "version": "2.15.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/filp/whoops.git",
-                "reference": "e864ac957acd66e1565f25efda61e37791a5db0b"
+                "reference": "aac9304c5ed61bf7b1b7a6064bf9806ab842ce73"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/filp/whoops/zipball/e864ac957acd66e1565f25efda61e37791a5db0b",
-                "reference": "e864ac957acd66e1565f25efda61e37791a5db0b",
+                "url": "https://api.github.com/repos/filp/whoops/zipball/aac9304c5ed61bf7b1b7a6064bf9806ab842ce73",
+                "reference": "aac9304c5ed61bf7b1b7a6064bf9806ab842ce73",
                 "shasum": ""
             },
             "require": {
             ],
             "support": {
                 "issues": "https://github.com/filp/whoops/issues",
-                "source": "https://github.com/filp/whoops/tree/2.15.1"
+                "source": "https://github.com/filp/whoops/tree/2.15.2"
             },
             "funding": [
                 {
                     "type": "github"
                 }
             ],
-            "time": "2023-03-06T18:09:13+00:00"
+            "time": "2023-04-12T12:00:00+00:00"
         },
         {
             "name": "hamcrest/hamcrest-php",
         },
         {
             "name": "nunomaduro/larastan",
-            "version": "2.5.1",
+            "version": "v2.6.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/nunomaduro/larastan.git",
-                "reference": "072e2c9566ae000bf66c92384fc933b81885244b"
+                "reference": "ccac5b25949576807862cf32ba1fce1769c06c42"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/nunomaduro/larastan/zipball/072e2c9566ae000bf66c92384fc933b81885244b",
-                "reference": "072e2c9566ae000bf66c92384fc933b81885244b",
+                "url": "https://api.github.com/repos/nunomaduro/larastan/zipball/ccac5b25949576807862cf32ba1fce1769c06c42",
+                "reference": "ccac5b25949576807862cf32ba1fce1769c06c42",
                 "shasum": ""
             },
             "require": {
                 "illuminate/support": "^9.47.0 || ^10.0.0",
                 "php": "^8.0.2",
                 "phpmyadmin/sql-parser": "^5.6.0",
-                "phpstan/phpstan": "~1.10.3"
+                "phpstan/phpstan": "~1.10.6"
             },
             "require-dev": {
                 "nikic/php-parser": "^4.15.2",
             ],
             "support": {
                 "issues": "https://github.com/nunomaduro/larastan/issues",
-                "source": "https://github.com/nunomaduro/larastan/tree/2.5.1"
+                "source": "https://github.com/nunomaduro/larastan/tree/v2.6.0"
             },
             "funding": [
                 {
                     "type": "patreon"
                 }
             ],
-            "time": "2023-03-04T23:46:40+00:00"
+            "time": "2023-04-20T12:40:01+00:00"
         },
         {
             "name": "phar-io/manifest",
         },
         {
             "name": "phpstan/phpstan",
-            "version": "1.10.6",
+            "version": "1.10.14",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpstan/phpstan.git",
-                "reference": "50d089a3e0904b0fe7e2cf2d4fd37d427d64235a"
+                "reference": "d232901b09e67538e5c86a724be841bea5768a7c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpstan/phpstan/zipball/50d089a3e0904b0fe7e2cf2d4fd37d427d64235a",
-                "reference": "50d089a3e0904b0fe7e2cf2d4fd37d427d64235a",
+                "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d232901b09e67538e5c86a724be841bea5768a7c",
+                "reference": "d232901b09e67538e5c86a724be841bea5768a7c",
                 "shasum": ""
             },
             "require": {
                 "static analysis"
             ],
             "support": {
+                "docs": "https://phpstan.org/user-guide/getting-started",
+                "forum": "https://github.com/phpstan/phpstan/discussions",
                 "issues": "https://github.com/phpstan/phpstan/issues",
-                "source": "https://github.com/phpstan/phpstan/tree/1.10.6"
+                "security": "https://github.com/phpstan/phpstan/security/policy",
+                "source": "https://github.com/phpstan/phpstan-src"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-03-09T16:55:12+00:00"
+            "time": "2023-04-19T13:47:27+00:00"
         },
         {
             "name": "phpunit/php-code-coverage",
         },
         {
             "name": "phpunit/phpunit",
-            "version": "9.6.5",
+            "version": "9.6.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "86e761949019ae83f49240b2f2123fb5ab3b2fc5"
+                "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/86e761949019ae83f49240b2f2123fb5ab3b2fc5",
-                "reference": "86e761949019ae83f49240b2f2123fb5ab3b2fc5",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c993f0d3b0489ffc42ee2fe0bd645af1538a63b2",
+                "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2",
                 "shasum": ""
             },
             "require": {
             ],
             "support": {
                 "issues": "https://github.com/sebastianbergmann/phpunit/issues",
-                "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.5"
+                "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
+                "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.7"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-03-09T06:34:10+00:00"
+            "time": "2023-04-14T08:58:40+00:00"
         },
         {
             "name": "sebastian/cli-parser",
index 46357038a23c082e4d472c072911afe14833f60e..c1e246955b2c2b65d577a7d620bc9b9611c4877a 100644 (file)
@@ -1,32 +1,35 @@
 #!/usr/bin/env node
 
 const esbuild = require('esbuild');
-const fs = require('fs');
 const path = require('path');
+const fs = require('fs');
 
 // Check if we're building for production
 // (Set via passing `production` as first argument)
 const isProd = process.argv[2] === 'production';
 
 // Gather our input files
-const jsInDir = path.join(__dirname, '../../resources/js');
-const jsInDirFiles = fs.readdirSync(jsInDir, 'utf8');
-const entryFiles = jsInDirFiles
-    .filter(f => f.endsWith('.js') || f.endsWith('.mjs'))
-    .map(f => path.join(jsInDir, f));
+const entryPoints = {
+    app: path.join(__dirname, '../../resources/js/app.js'),
+    code: path.join(__dirname, '../../resources/js/code/index.mjs'),
+    'legacy-modes': path.join(__dirname, '../../resources/js/code/legacy-modes.mjs'),
+};
 
 // Locate our output directory
-const outDir = path.join(__dirname, '../../public/dist');
+const outdir = path.join(__dirname, '../../public/dist');
 
 // Build via esbuild
 esbuild.build({
     bundle: true,
-    entryPoints: entryFiles,
-    outdir: outDir,
+    metafile: true,
+    entryPoints,
+    outdir,
     sourcemap: true,
     target: 'es2020',
     mainFields: ['module', 'main'],
     format: 'esm',
     minify: isProd,
     logLevel: "info",
+}).then(result => {
+    fs.writeFileSync('esbuild-meta.json', JSON.stringify(result.metafile));
 }).catch(() => process.exit(1));
\ No newline at end of file
index b68f2664aa38762df7743f8628e4dc474bcf3c6e..a68ae50b47e8598d1fdeec41dbfc80b0d0acbcc8 100644 (file)
@@ -33,6 +33,10 @@ If the codebase needs to be tested with deprecations, this can be done via uncom
 
 ## Code Standards
 
+We use tools to manage code standards and formatting within the project. If submitting a PR, formatting as per our project standards would help for clarity but don't worry too much about using/understanding these tools as we can always address issues at a later stage when they're picked up by our automated tools.
+
+### PHP
+
 PHP code standards are managed by [using PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer).
 Static analysis is in place using [PHPStan](https://phpstan.org/) & [Larastan](https://github.com/nunomaduro/larastan).
 The below commands can be used to utilise these tools:
@@ -51,7 +55,19 @@ composer format
 composer check-static
 ```
 
-If submitting a PR, formatting as per our project standards would help for clarity but don't worry too much about using/understanding these tools as we can always address issues at a later stage when they're picked up by our automated tools.
+### JavaScript
+
+JavaScript code standards use managed using [ESLint](https://eslint.org/).
+The ESLint rule configuration is managed within the `package.json` file.
+The below commands can be used to lint and format:
+
+```bash
+# Run code linting using ESLint
+npm run lint
+
+# Fix code where possible using ESLint
+npm run fix
+```
 
 ## Development using Docker
 
index a1092ce92569ffad21aec73471fbde7b8b621b93..9820839a45d1c094e72908dd7e9e2a6ef6c03a0e 100644 (file)
@@ -115,6 +115,7 @@ There are various global helper libraries in BookStack which can be accessed via
 
 ```js
 // HTTP service
+// Relative URLs will be resolved against the instance BASE_URL
 window.$http.get(url, params);
 window.$http.post(url, data);
 window.$http.put(url, data);
@@ -153,4 +154,10 @@ window.$components.get(name);
 // Get the first active component of the given name that's been
 // created on the given element.
 window.$components.firstOnElement(element, name);
-```
\ No newline at end of file
+```
+
+## Public Events
+
+There are a range of available events that are emitted as part of a public & supported API for accessing or extending JavaScript libraries & components used in the system.
+
+Details on these events can be found in the [JavaScript Public Events file](javascript-public-events.md).
\ No newline at end of file
diff --git a/dev/docs/javascript-public-events.md b/dev/docs/javascript-public-events.md
new file mode 100644 (file)
index 0000000..1e95dc8
--- /dev/null
@@ -0,0 +1,255 @@
+# JavaScript Public Events
+
+There are a range of available events emitted as part of a public & [supported](#support) API for accessing or extending JavaScript libraries and components used in the system.
+These are emitted via standard DOM events so can be consumed using standard DOM APIs like so:
+
+```javascript
+window.addEventListener('event-name', event => {
+   const eventData = event.detail; 
+});
+```
+
+Such events are typically emitted from a DOM element relevant to event, which then bubbles up.
+For most use-cases you can probably just listen on the `window` as shown above.
+
+## Support
+
+This event system, and the events emitted, are considered semi-supported.
+Breaking changes of the event API, event names, or event properties, are possible but will be documented in update notes.
+The detail provided within the events, and the libraries made accessible, are not considered supported nor stable, and changes to these won't be clearly documented changelogs.
+
+## Event Naming Scheme
+
+Events are typically named in the following format:
+
+```text
+<context>::<action/lifecycle>
+
+# Examples:
+editor-tinymce::setup
+library-cm6::configure-theme
+```
+
+If the event is generic in use but specific to a library, the `<context>` will start with `library-` followed by the library name. Otherwise `<context>` may reflect the UI context/component.
+
+The `<action/lifecycle>` reflects the lifecycle stage of the context, or a specific action to perform if the event is specific to a certain use-case.
+
+## Event Listing
+
+### `editor-markdown-cm6::pre-init`
+
+This event is called before the markdown input editor CodeMirror instance is created or loaded.
+
+#### Event Data
+
+- `editorViewConfig` - An [EditorViewConfig](https://codemirror.net/docs/ref/#view.EditorViewConfig) object that will eventially be passed when creating the CodeMirror EditorView instance.
+
+##### Example
+
+```javascript
+// Always load the editor with specific pre-defined content if empty
+window.addEventListener('editor-markdown-cm6::pre-init', event => {
+    const config = event.detail.editorViewConfig;
+    config.doc = config.doc || "Start this page with a nice story";
+});
+```
+
+### `editor-markdown::setup`
+
+This event is called when the markdown editor loads, post configuration but before the editor is ready to use.
+
+#### Event Data
+
+- `markdownIt` - A references to the [MarkdownIt](https://markdown-it.github.io/markdown-it/#MarkdownIt) instance used to render markdown to HTML (Just for the preview).
+- `displayEl` - The IFrame Element that wraps the HTML preview display.
+- `cmEditorView` - The CodeMirror [EditorView](https://codemirror.net/docs/ref/#view.EditorView) instance used for the markdown input editor.
+
+##### Example
+
+```javascript
+// Set all text in the display to be red by default.
+window.addEventListener('editor-markdown::setup', event => {
+    const display = event.detail.displayEl;
+    display.contentDocument.body.style.color = 'red';
+});
+```
+
+### `editor-drawio::configure`
+
+This event is called as the embedded diagrams.net drawing editor loads, to allow configuration of the diagrams.net interface.
+See [this diagrams.net page](https://www.diagrams.net/doc/faq/configure-diagram-editor) for details on the available options for the configure event.
+
+If using a custom diagrams.net instance, via the `DRAWIO` option, you will need to ensure  your DRAWIO option URL has the `configure=1` query parameter.
+
+#### Event Data
+
+- `config` - The configuration object that will be passed to diagrams.net.
+  - This will likely be empty by default, but modify this object in-place as needed with your desired options.
+
+##### Example
+
+```javascript
+// Set only the "general" and "android" libraries to show by default
+window.addEventListener('editor-drawio::configure', event => {
+    const config = event.detail.config;
+    config.enabledLibraries = ["general", "android"];
+});
+```
+
+### `editor-tinymce::pre-init`
+
+This event is called before the TinyMCE editor, used as the BookStack WYSIWYG page editor, is initialised.
+
+#### Event Data
+
+- `config` - Object containing the configuration that's going to be passed to [tinymce.init](https://www.tiny.cloud/docs/api/tinymce/root_tinymce/#init).
+
+##### Example
+
+```javascript
+// Removed "bold" from the editor toolbar
+window.addEventListener('editor-tinymce::pre-init', event => {
+    const tinyConfig = event.detail.config;
+    tinyConfig.toolbar = tinyConfig.toolbar.replace('bold ', '');
+});
+```
+
+### `editor-tinymce::setup`
+
+This event is called during the `setup` lifecycle stage of the TinyMCE editor used as the BookStack WYSIWYG editor. This is after configuration, but before the editor is fully loaded and ready to use. 
+
+##### Event Data
+
+- `editor` - The [tinymce.Editor](https://www.tiny.cloud/docs/api/tinymce/tinymce.editor/) instance used for the WYSIWYG editor.
+
+##### Example
+
+```javascript
+// Replaces the editor content with redacted message 3 seconds after load.
+window.addEventListener('editor-tinymce::setup', event => {
+    const editor = event.detail.editor;
+    setTimeout(() => {
+        editor.setContent('REDACTED!');
+    }, 3000);
+});
+```
+
+### `library-cm6::configure-theme`
+
+This event is called whenever a CodeMirror instance is loaded, as a method to configure the theme used by CodeMirror. This applies to all CodeMirror instances including in-page code blocks, editors using in BookStack settings, and the Page markdown editor.
+
+#### Event Data
+
+- `darkModeActive` - A boolean to indicate if the current view/page is being loaded with dark mode active.
+- `registerViewTheme(builder)` - A method that can be called to register a new view (CodeMirror UI) theme.
+  - `builder` - A function that will return  an object that will be passed into the CodeMirror [EditorView.theme()](https://codemirror.net/docs/ref/#view.EditorView^theme) function as a StyleSpec.
+- `registerHighlightStyle(builder)` - A method that can be called to register a new HighlightStyle (code highlighting) theme.
+  - `builder` - A function, that receives a reference to [Tag.tags](https://lezer.codemirror.net/docs/ref/#highlight.tags) and returns an array of [TagStyle](https://codemirror.net/docs/ref/#language.TagStyle) objects.
+
+##### Example
+
+The below shows registering a custom "Solarized dark" editor and syntax theme:
+
+<details>
+<summary>Show Example</summary>
+
+```javascript
+// Theme data taken from:
+// https://github.com/craftzdog/cm6-themes/blob/main/packages/solarized-dark/src/index.ts
+// (MIT License) - Copyright (C) 2022 by Takuya Matsuyama and others
+const base00 = '#002b36',
+    base01 = '#073642',
+    base02 = '#586e75',
+    base03 = '#657b83',
+    base04 = '#839496',
+    base05 = '#93a1a1',
+    base06 = '#eee8d5',
+    base07 = '#fdf6e3',
+    base_red = '#dc322f',
+    base_orange = '#cb4b16',
+    base_yellow = '#b58900',
+    base_green = '#859900',
+    base_cyan = '#2aa198',
+    base_blue = '#268bd2',
+    base_violet = '#6c71c4',
+    base_magenta = '#d33682'
+
+const invalid = '#d30102',
+    stone = base04,
+    darkBackground = '#00252f',
+    highlightBackground = '#173541',
+    background = base00,
+    tooltipBackground = base01,
+    selection = '#173541',
+    cursor = base04
+
+function viewThemeBuilder() {
+    return {
+      '&':{color:base05,backgroundColor:background},
+      '.cm-content':{caretColor:cursor},
+      '.cm-cursor, .cm-dropCursor':{borderLeftColor:cursor},
+      '&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection':{backgroundColor:selection},
+      '.cm-panels':{backgroundColor:darkBackground,color:base03},
+      '.cm-panels.cm-panels-top':{borderBottom:'2px solid black'},
+      '.cm-panels.cm-panels-bottom':{borderTop:'2px solid black'},
+      '.cm-searchMatch':{backgroundColor:'#72a1ff59',outline:'1px solid #457dff'},
+      '.cm-searchMatch.cm-searchMatch-selected':{backgroundColor:'#6199ff2f'},
+      '.cm-activeLine':{backgroundColor:highlightBackground},
+      '.cm-selectionMatch':{backgroundColor:'#aafe661a'},
+      '&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket':{outline:`1px solid ${base06}`},
+      '.cm-gutters':{backgroundColor:darkBackground,color:stone,border:'none'},
+      '.cm-activeLineGutter':{backgroundColor:highlightBackground},
+      '.cm-foldPlaceholder':{backgroundColor:'transparent',border:'none',color:'#ddd'},
+      '.cm-tooltip':{border:'none',backgroundColor:tooltipBackground},
+      '.cm-tooltip .cm-tooltip-arrow:before':{borderTopColor:'transparent',borderBottomColor:'transparent'},
+      '.cm-tooltip .cm-tooltip-arrow:after':{borderTopColor:tooltipBackground,borderBottomColor:tooltipBackground},
+      '.cm-tooltip-autocomplete':{
+        '& > ul > li[aria-selected]':{backgroundColor:highlightBackground,color:base03}
+      }
+    };
+}
+
+function highlightStyleBuilder(t) {
+    return [{tag:t.keyword,color:base_green},
+      {tag:[t.name,t.deleted,t.character,t.propertyName,t.macroName],color:base_cyan},
+      {tag:[t.variableName],color:base05},
+      {tag:[t.function(t.variableName)],color:base_blue},
+      {tag:[t.labelName],color:base_magenta},
+      {tag:[t.color,t.constant(t.name),t.standard(t.name)],color:base_yellow},
+      {tag:[t.definition(t.name),t.separator],color:base_cyan},
+      {tag:[t.brace],color:base_magenta},
+      {tag:[t.annotation],color:invalid},
+      {tag:[t.number,t.changed,t.annotation,t.modifier,t.self,t.namespace],color:base_magenta},
+      {tag:[t.typeName,t.className],color:base_orange},
+      {tag:[t.operator,t.operatorKeyword],color:base_violet},
+      {tag:[t.tagName],color:base_blue},
+      {tag:[t.squareBracket],color:base_red},
+      {tag:[t.angleBracket],color:base02},
+      {tag:[t.attributeName],color:base05},
+      {tag:[t.regexp],color:invalid},
+      {tag:[t.quote],color:base_green},
+      {tag:[t.string],color:base_yellow},
+      {tag:t.link,color:base_cyan,textDecoration:'underline',textUnderlinePosition:'under'},
+      {tag:[t.url,t.escape,t.special(t.string)],color:base_yellow},
+      {tag:[t.meta],color:base_red},
+      {tag:[t.comment],color:base02,fontStyle:'italic'},
+      {tag:t.strong,fontWeight:'bold',color:base06},
+      {tag:t.emphasis,fontStyle:'italic',color:base_green},
+      {tag:t.strikethrough,textDecoration:'line-through'},
+      {tag:t.heading,fontWeight:'bold',color:base_yellow},
+      {tag:t.heading1,fontWeight:'bold',color:base07},
+      {tag:[t.heading2,t.heading3,t.heading4],fontWeight:'bold',color:base06},
+      {tag:[t.heading5,t.heading6],color:base06},
+      {tag:[t.atom,t.bool,t.special(t.variableName)],color:base_magenta},
+      {tag:[t.processingInstruction,t.inserted,t.contentSeparator],color:base_red},
+      {tag:[t.contentSeparator],color:base_yellow},
+      {tag:t.invalid,color:base02,borderBottom:`1px dotted ${base_red}`}];
+}
+
+window.addEventListener('library-cm6::configure-theme', event => {
+    const detail = event.detail;
+    detail.registerViewTheme(viewThemeBuilder);
+    detail.registerHighlightStyle(highlightStyleBuilder);
+});
+```
+</details>
\ No newline at end of file
index 78a1c9b292ba54d3c96a3942ecb436b87247e243..ca1b603bb11e81579aef0b6c76c99ec1c70b6904 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index af7497e12dc95a7f095cd63bf3fba774a302fb54..5f281acb526d5613784900991e0691f67e7c1461 100644 (file)
@@ -17,18 +17,18 @@ return [
     'page_move'                   => 'преместена страница',
 
     // Chapters
-    'chapter_create'              => 'създадена страница',
-    'chapter_create_notification' => 'Ð\93лаваÑ\82а Ðµ Ð´Ð¾Ð±Ð°Ð²ÐµÐ½Ð° Ñ\83Ñ\81пеÑ\88но',
+    'chapter_create'              => 'създадена глава',
+    'chapter_create_notification' => 'УÑ\81пеÑ\88но Ñ\81Ñ\8aздадена Ð³Ð»Ð°Ð²Ð°',
     'chapter_update'              => 'обновена глава',
-    'chapter_update_notification' => 'Ð\93лаваÑ\82а Ðµ Ð¾Ð±Ð½Ð¾Ð²ÐµÐ½Ð° Ñ\83Ñ\81пеÑ\88но',
+    'chapter_update_notification' => 'УÑ\81пеÑ\88но Ð¾Ð±Ð½Ð¾Ð²ÐµÐ½Ð° Ð³Ð»Ð°Ð²Ð°',
     'chapter_delete'              => 'изтрита глава',
-    'chapter_delete_notification' => 'Ð\93лаваÑ\82а Ðµ Ð¸Ð·Ñ\82Ñ\80иÑ\82а Ñ\83Ñ\81пеÑ\88но',
+    'chapter_delete_notification' => 'УÑ\81пеÑ\88но Ð¸Ð·Ñ\82Ñ\80иÑ\82а Ð³Ð»Ð°Ð²Ð°',
     'chapter_move'                => 'преместена глава',
 
     // Books
     'book_create'                 => 'създадена книга',
     'book_create_notification'    => 'Книгата е създадена успешно',
-    'book_create_from_chapter'              => 'converted chapter to book',
+    'book_create_from_chapter'              => 'превърната глава в книга',
     'book_create_from_chapter_notification' => 'Chapter successfully converted to a book',
     'book_update'                 => 'обновена книга',
     'book_update_notification'    => 'Книгата е обновена успешно',
@@ -68,9 +68,9 @@ return [
     'user_delete_notification' => 'Потребителят е премахнат успешно',
 
     // Roles
-    'role_create_notification' => 'Role successfully created',
-    'role_update_notification' => 'Role successfully updated',
-    'role_delete_notification' => 'Role successfully deleted',
+    'role_create_notification' => 'Успешна създадена роля',
+    'role_update_notification' => 'Успешно обновена роля',
+    'role_delete_notification' => 'Успешно изтрита роля',
 
     // Other
     'commented_on'                => 'коментирано на',
index dd3c1566acf3a0e17654ca6167b88fc892d4a910..ec3e753513d9d475764585191358bc1eb0d2a711 100644 (file)
@@ -25,23 +25,23 @@ return [
     'forgot_password' => 'Забравена парола?',
     'remember_me' => 'Запомни ме',
     'ldap_email_hint' => 'Моля въведете емейл, който да използвате за дадения профил.',
-    'create_account' => 'СÑ\8aздай Ð\90каунт',
+    'create_account' => 'СÑ\8aздаване Ð½Ð° Ð°каунт',
     'already_have_account' => 'Вече имате профил?',
-    'dont_have_account' => 'Нямате акаунт?',
+    'dont_have_account' => 'Ð\9dÑ\8fмаÑ\82е Ð»Ð¸ Ð°ÐºÐ°Ñ\83нÑ\82?',
     'social_login' => 'Влизане по друг начин',
     'social_registration' => 'Регистрация по друг начин',
-    'social_registration_text' => 'РегиÑ\81Ñ\82Ñ\80аÑ\86иÑ\8f Ð¸ Ð²Ð¿Ð¸Ñ\81ване Ñ\87Ñ\80ез друга услуга.',
+    'social_registration_text' => 'РегиÑ\81Ñ\82Ñ\80иÑ\80ане Ð¸ Ð²Ð»Ð¸Ð·Ð°Ð½Ðµ Ð¿Ð¾Ñ\81Ñ\80едÑ\81Ñ\82вом друга услуга.',
 
-    'register_thanks' => 'Ð\91лагодаÑ\80им Ð\92и Ð·Ð° Ñ\80егиÑ\81Ñ\82Ñ\80аÑ\86иÑ\8fÑ\82а!',
+    'register_thanks' => 'Ð\91лагодаÑ\80ноÑ\81Ñ\82и Ð·Ð° Ñ\80егиÑ\81Ñ\82Ñ\80иÑ\80анеÑ\82о!',
     'register_confirm' => 'Моля, провери своя имейл адрес и натисни бутона за потвърждение, за да достъпиш :appName.',
     'registrations_disabled' => 'Регистрациите към момента са забранени',
     'registration_email_domain_invalid' => 'Този емейл домейн към момента няма достъп до приложението',
     'register_success' => 'Благодарим Ви за регистрацията! В момента сте регистриран и сте вписани в приложението.',
 
     // Login auto-initiation
-    'auto_init_starting' => 'Attempting Login',
-    'auto_init_starting_desc' => 'We\'re contacting your authentication system to start the login process. If there\'s no progress after 5 seconds you can try clicking the link below.',
-    'auto_init_start_link' => 'Proceed with authentication',
+    'auto_init_starting' => 'Опит за вход в системата',
+    'auto_init_starting_desc' => 'Свързахме системата ви за удостоверяване към началото на процеса при влизане. Ако няма напредък след 5 секунди, то може да опитате да щракнете върху долната връзка.',
+    'auto_init_start_link' => 'Продължаване с удостоверяването',
 
     // Password Reset
     'reset_password' => 'Нулиране на паролата',
@@ -56,13 +56,13 @@ return [
     // Email Confirmation
     'email_confirm_subject' => 'Потвърди емейла си за :appName',
     'email_confirm_greeting' => 'Благодарим Ви, че се присъединихте към :appName!',
-    'email_confirm_text' => 'Ð\9cолÑ\8f, Ð¿Ð¾Ñ\82вÑ\8aÑ\80деÑ\82е Ð²Ð°Ñ\88иÑ\8f Ð¸Ð¼ÐµÐ¹Ð» Ð°Ð´Ñ\80еÑ\81, ÐºÐ°Ñ\82о Ñ\81ледваÑ\82е връзката по-долу:',
+    'email_confirm_text' => 'Ð\9fоÑ\82вÑ\8aÑ\80деÑ\82е Ð°Ð´Ñ\80еÑ\81а Ð½Ð° Ð¸Ð¼ÐµÐ¹Ð»Ð° Ñ\81и, Ñ\89Ñ\80аквайки Ð²Ñ\8aÑ\80Ñ\85Ñ\83 връзката по-долу:',
     'email_confirm_action' => 'Потвърдете имейл',
     'email_confirm_send_error' => 'Нужно ви е потвърждение чрез емейл, но системата не успя да го изпрати. Моля свържете се с администратора, за да проверите дали вашият емейл адрес е конфигуриран правилно.',
     'email_confirm_success' => 'Имейлът ти е потвърден! Вече би трябвало да можеш да се впишеш с този имейл адрес.',
-    'email_confirm_resent' => 'Ð\91еÑ\88е Ð¸Ð·Ð¿Ñ\80аÑ\82ен Ð¸Ð¼ÐµÐ¹Ð» Ñ\81 Ð¿Ð¾Ñ\82вÑ\8aÑ\80ждение, Ð\9cолÑ\8f, проверете кутията си.',
-    'email_confirm_thanks' => 'Thanks for confirming!',
-    'email_confirm_thanks_desc' => 'Please wait a moment while your confirmation is handled. If you are not redirected after 3 seconds press the "Continue" link below to proceed.',
+    'email_confirm_resent' => 'Ð\95-пиÑ\81мо Ð·Ð° Ð¿Ð¾Ñ\82вÑ\8aÑ\80ждение Ðµ Ð¸Ð·Ð¿Ñ\80аÑ\82ено Ð¿Ð°Ðº, проверете кутията си.',
+    'email_confirm_thanks' => 'Благодарности за потвърждаването!',
+    'email_confirm_thanks_desc' => 'Почакайте малко, обработвайки потвърждението ви. Ако не сте пренасочени след 3 секунди, то натиснете долу връзката "Продължаване", за да продължите.',
 
     'email_not_confirmed' => 'Имейл адресът не е потвърден',
     'email_not_confirmed_text' => 'Вашият емейл адрес все още не е потвърден.',
@@ -74,9 +74,9 @@ return [
     'user_invite_email_subject' => 'Вие бяхте поканен да се присъедините към :appName!',
     'user_invite_email_greeting' => 'Беше създаден акаунт за Вас във :appName.',
     'user_invite_email_text' => 'Натисните бутона по-долу за да определите парола и да получите достъп:',
-    'user_invite_email_action' => 'Ð\9fарола на акаунта',
+    'user_invite_email_action' => 'Ð\97адаване Ð½Ð° Ð¿арола на акаунта',
     'user_invite_page_welcome' => 'Добре дошли в :appName!',
-    'user_invite_page_text' => 'За да финализирате вашият акаунт и да получите достъп трябва да определите парола, която да бъде използвана за следващия влизания в :appName.',
+    'user_invite_page_text' => 'За да довършвам окончателно акаунта ви и да получите достъп трябва да зададете парола, която ще се използва за влизане в :appName при бъдещи посещения.',
     'user_invite_page_confirm_button' => 'Потвърди паролата',
     'user_invite_success_login' => 'Паролата е настроена, вече можеш да се впишеш с новата парола, за да достъпиш :appName!',
 
index 0b065ecc921a1f7a3a89990fc079736444f55cec..7d8db89538deaaf7f461ec086486db9e4689a8f9 100644 (file)
@@ -6,11 +6,11 @@ return [
 
     // Buttons
     'cancel' => 'Отказ',
-    'confirm' => 'Ð\9fоÑ\82вÑ\8aÑ\80ди',
+    'confirm' => 'Ð\9fоÑ\82вÑ\8aÑ\80ждаване',
     'back' => 'Назад',
-    'save' => 'Ð\97апази',
-    'continue' => 'Ð\9fÑ\80одÑ\8aлжи',
-    'select' => 'Ð\98збеÑ\80и',
+    'save' => 'Ð\97апиÑ\81',
+    'continue' => 'Ð\9fÑ\80одÑ\8aлжаване',
+    'select' => 'Ð\98збеÑ\80еÑ\82е',
     'toggle_all' => 'Избери всички',
     'more' => 'Повече',
 
@@ -18,38 +18,38 @@ return [
     'name' => 'Име',
     'description' => 'Описание',
     'role' => 'Роля',
-    'cover_image' => 'Основно изображение',
-    'cover_image_description' => 'Ð\9aаÑ\80Ñ\82инаÑ\82а Ñ\82Ñ\80Ñ\8fбва Ð´Ð° Ðµ Ð¿Ñ\80иблизиÑ\82елно 440х250 пиксела.',
+    'cover_image' => 'Образ на корицата',
+    'cover_image_description' => 'Ð\9eбÑ\80азÑ\8aÑ\82 Ñ\82Ñ\80Ñ\8fбва Ð´Ð° Ðµ Ð³Ð¾Ñ\80е-долÑ\83 440х250 пиксела.',
 
     // Actions
     'actions' => 'Действия',
     'view' => 'Преглед',
     'view_all' => 'Преглед на всички',
-    'new' => 'New',
-    'create' => 'СÑ\8aздай',
+    'new' => 'Ново',
+    'create' => 'СÑ\8aздаване',
     'update' => 'Обновяване',
     'edit' => 'Редактиране',
     'sort' => 'Сортиране',
     'move' => 'Преместване',
-    'copy' => 'Ð\9aопиÑ\80ай',
-    'reply' => 'Отговори',
-    'delete' => 'Ð\98зÑ\82Ñ\80ий',
-    'delete_confirm' => 'Ð\9fоÑ\82вÑ\8aÑ\80деÑ\82е изтриването',
-    'search' => 'ТÑ\8aÑ\80Ñ\81и',
-    'search_clear' => 'Ð\98зÑ\87иÑ\81Ñ\82и търсенето',
-    'reset' => 'Ð\9dÑ\83лиÑ\80ай',
+    'copy' => 'Ð\9aопиÑ\80ане',
+    'reply' => 'Отговор',
+    'delete' => 'Ð\98зÑ\82Ñ\80иване',
+    'delete_confirm' => 'Ð\9fоÑ\82вÑ\8aÑ\80ждаване Ð½Ð° изтриването',
+    'search' => 'ТÑ\8aÑ\80Ñ\81ене',
+    'search_clear' => 'Ð\98зÑ\87иÑ\81Ñ\82ване Ð½Ð° търсенето',
+    'reset' => 'Ð\9dÑ\83лиÑ\80ане',
     'remove' => 'Премахване',
-    'add' => 'Добави',
-    'configure' => 'Ð\9aонÑ\84игÑ\83Ñ\80иÑ\80ай',
-    'fullscreen' => 'Ð\9fÑ\8aлен екран',
-    'favourite' => 'Ð\94обави Ð² Ð»Ñ\8eбими',
-    'unfavourite' => 'Ð\9fÑ\80емаÑ\85ни Ð¾Ñ\82 Ð»Ñ\8eбими',
-    'next' => 'СледваÑ\89',
+    'add' => 'Добавяне',
+    'configure' => 'Ð\9aонÑ\84игÑ\83Ñ\80иÑ\80ане',
+    'fullscreen' => 'ЦÑ\8fл екран',
+    'favourite' => 'Ð\9bÑ\8eбимо',
+    'unfavourite' => 'Ð\9dе Ðµ Ð»Ñ\8eбимо',
+    'next' => 'Ð\9dапÑ\80ед',
     'previous' => 'Предишен',
     'filter_active' => 'Активен филтър:',
-    'filter_clear' => 'Ð\98зÑ\87иÑ\81Ñ\82и Ñ\84илÑ\82Ñ\8aÑ\80а',
-    'download' => 'Download',
-    'open_in_tab' => 'Open in Tab',
+    'filter_clear' => 'Ð\98зÑ\87иÑ\81Ñ\82ване Ð½Ð° Ñ\84илÑ\82Ñ\80иÑ\82е',
+    'download' => 'Изтегляне',
+    'open_in_tab' => 'Отваряне в раздел',
 
     // Sort Options
     'sort_options' => 'Опции за сортиране',
@@ -74,27 +74,27 @@ return [
     'list_view' => 'Изглед списък',
     'default' => 'Основен',
     'breadcrumb' => 'Трасиране',
-    'status' => 'СÑ\82аÑ\82Ñ\83Ñ\81',
+    'status' => 'СÑ\8aÑ\81Ñ\82оÑ\8fние',
     'status_active' => 'Активен',
     'status_inactive' => 'Неактивен',
     'never' => 'Никога',
-    'none' => 'Няма',
+    'none' => 'Нищо',
 
     // Header
-    'homepage' => 'Homepage',
+    'homepage' => 'Начална страница',
     'header_menu_expand' => 'Разшири заглавното меню',
-    'profile_menu' => 'Ð\9fÑ\80оÑ\84ил Ð¼ÐµÐ½Ñ\8e',
-    'view_profile' => 'Разглеждане Ð½Ð° Ð¿Ñ\80оÑ\84ил',
+    'profile_menu' => 'Ð\9cенÑ\8e Ð½Ð° Ð¿Ñ\80оÑ\84ила',
+    'view_profile' => 'Ð\9fÑ\80еглед Ð½Ð° Ð¿Ñ\80оÑ\84ила',
     'edit_profile' => 'Редактиране на профила',
     'dark_mode' => 'Тъмен режим',
     'light_mode' => 'Светъл режим',
-    'global_search' => 'Global Search',
+    'global_search' => 'Глобално търсене',
 
     // Layout tabs
-    'tab_info' => 'Информация',
-    'tab_info_label' => 'Таб: Ð\9fокажи вторична информация',
+    'tab_info' => 'Инфо.',
+    'tab_info_label' => 'Раздел: Ð¿Ð¾ÐºÐ°Ð·Ð²Ð° вторична информация',
     'tab_content' => 'Съдържание',
-    'tab_content_label' => 'Таб: Ð\9fокажи първично съдържание',
+    'tab_content_label' => 'Раздел: Ð\9fоказва първично съдържание',
 
     // Email Content
     'email_action_help' => 'Ако имате проблеми с бутона ":actionText" по-горе, копирайте и поставете URL адреса по-долу в уеб браузъра си:',
@@ -102,6 +102,6 @@ return [
 
     // Footer Link Options
     // Not directly used but available for convenience to users.
-    'privacy_policy' => 'Ð\9bиÑ\87ни Ð´Ð°Ð½Ð½Ð¸',
-    'terms_of_service' => 'Ð\9eбÑ\89и Ñ\83Ñ\81ловиÑ\8f',
+    'privacy_policy' => 'Ð\9fолиÑ\82ика Ð·Ð° Ð¿Ð¾Ð²ÐµÑ\80иÑ\82елноÑ\81Ñ\82',
+    'terms_of_service' => 'УÑ\81ловиÑ\8f Ð½Ð° Ñ\83Ñ\81лÑ\83гаÑ\82а',
 ];
index e47f629d44d431ebcbab7c8a92f1c68862a6fa9f..f677afa631efa05137e4d7bd5e7b2a9b995d8dc0 100644 (file)
@@ -9,22 +9,22 @@ return [
     // General editor terms
     'general' => 'Общи',
     'advanced' => 'Разширени',
-    'none' => 'Няма',
-    'cancel' => 'Ð\9eÑ\82кажи',
-    'save' => 'Ð\97апази',
-    'close' => 'Ð\97аÑ\82воÑ\80и',
-    'undo' => 'Отмени',
-    'redo' => 'Преправи',
+    'none' => 'Нищо',
+    'cancel' => 'Ð\9eÑ\82каз',
+    'save' => 'Ð\97апиÑ\81',
+    'close' => 'Ð\97аÑ\82ваÑ\80Ñ\8fне',
+    'undo' => 'Отмяна',
+    'redo' => 'Повтаряне',
     'left' => 'Вляво',
     'center' => 'По средата',
     'right' => 'Вдясно',
     'top' => 'Отгоре',
     'middle' => 'Среда',
     'bottom' => 'Отдолу',
-    'width' => 'ШиÑ\80оÑ\87ина',
+    'width' => 'Ширина',
     'height' => 'Височина',
     'More' => 'Още',
-    'select' => 'Select...',
+    'select' => 'Изберете...',
 
     // Toolbar
     'formats' => 'Формати',
@@ -50,9 +50,9 @@ return [
     'custom_color' => 'Цвят по избор',
     'remove_color' => 'Премахване на цвят',
     'background_color' => 'Фонов цвят',
-    'align_left' => 'Приравни вляво',
-    'align_center' => 'Приравни в центъра',
-    'align_right' => 'Приравни вдясно',
+    'align_left' => 'Подравняване отляво',
+    'align_center' => 'Подравняване в средата',
+    'align_right' => 'Подравняване отдясно',
     'align_justify' => 'Justify',
     'list_bullet' => 'Списък',
     'list_numbered' => 'Номериран списък',
@@ -60,38 +60,38 @@ return [
     'indent_increase' => 'Увеличаване на отстъпа',
     'indent_decrease' => 'Намаляване на отстъпа',
     'table' => 'Таблица',
-    'insert_image' => 'Ð\92мÑ\8aкни Ð¸Ð·Ð¾Ð±Ñ\80ажение',
-    'insert_image_title' => 'Ð\92мÑ\8aкни/Ñ\80едакÑ\82иÑ\80ай Ð¸Ð·Ð¾Ð±Ñ\80ажение',
-    'insert_link' => 'Ð\92мÑ\8aкни/Ñ\80едакÑ\82иÑ\80ай връзка',
+    'insert_image' => 'Ð\92мÑ\8aкване Ð½Ð° Ð¾Ð±Ñ\80аз',
+    'insert_image_title' => 'Ð\92мÑ\8aкване/Ñ\80едакÑ\82иÑ\80ане Ð½Ð° Ð¾Ð±Ñ\80аз',
+    'insert_link' => 'Ð\92мÑ\8aкване/Ñ\80едакÑ\82иÑ\80ане Ð½Ð° връзка',
     'insert_link_title' => 'Вмъкни/редактирай връзка',
-    'insert_horizontal_line' => 'Ð\92мÑ\8aкни хоризонтална линия',
-    'insert_code_block' => 'Въведи код',
+    'insert_horizontal_line' => 'Ð\92мÑ\8aкване Ð½Ð° хоризонтална линия',
+    'insert_code_block' => 'Вмъкване на блок код',
     'edit_code_block' => 'Edit code block',
-    'insert_drawing' => 'Ð\92мÑ\8aкни/Ñ\80едакÑ\82иÑ\80ай рисунка',
+    'insert_drawing' => 'Ð\92мÑ\8aкване/Ñ\80едакÑ\82иÑ\80ане Ð½Ð° рисунка',
     'drawing_manager' => 'Управление на рисунките',
-    'insert_media' => 'Ð\92мÑ\8aкни/Ñ\80едакÑ\82иÑ\80ай Ð¼Ñ\83лÑ\82имедиÑ\8f',
-    'insert_media_title' => 'Ð\92мÑ\8aкни/Ñ\80едакÑ\82иÑ\80ай Ð¼Ñ\83лÑ\82имедиÑ\8f',
-    'clear_formatting' => 'Ð\98зÑ\87иÑ\81Ñ\82и форматирането',
+    'insert_media' => 'Ð\92мÑ\8aкване/Ñ\80едакÑ\82иÑ\80ане Ð½Ð° Ð¼Ñ\83лÑ\82имедиÑ\8fÑ\82а',
+    'insert_media_title' => 'Ð\92мÑ\8aкване/Ñ\80едакÑ\82иÑ\80ане Ð½Ð° Ð¼Ñ\83лÑ\82имедиÑ\8fÑ\82а',
+    'clear_formatting' => 'Ð\98зÑ\87иÑ\81Ñ\82ване Ð½Ð° форматирането',
     'source_code' => 'Изходен код',
     'source_code_title' => 'Изходен код',
     'fullscreen' => 'Цял екран',
-    'image_options' => 'Ð\9dаÑ\81Ñ\82Ñ\80ойки Ð½Ð° Ð¸Ð·Ð¾Ð±Ñ\80ажениеÑ\82о',
+    'image_options' => 'Ð\92Ñ\8aзможноÑ\81Ñ\82и Ð½Ð° Ð¾Ð±Ñ\80аза',
 
     // Tables
-    'table_properties' => 'Ð\9dаÑ\81Ñ\82Ñ\80ойки на таблицата',
-    'table_properties_title' => 'Ð\9dаÑ\81Ñ\82Ñ\80ойки Ð½Ð° Ñ\82аблиÑ\86аÑ\82а',
-    'delete_table' => 'Ð\98зÑ\82Ñ\80ий Ñ\82аблиÑ\86аÑ\82а',
+    'table_properties' => 'СвойÑ\81Ñ\82ва на таблицата',
+    'table_properties_title' => 'СвойÑ\81Ñ\82ва Ð½Ð° Ñ\82аблиÑ\86а',
+    'delete_table' => 'Ð\98зÑ\82Ñ\80иване Ð½Ð° Ñ\82аблиÑ\86а',
     'insert_row_before' => 'Вмъкни реда преди',
     'insert_row_after' => 'Вмъкни реда след',
-    'delete_row' => 'Ð\98зÑ\82Ñ\80ий Ñ\80еда',
+    'delete_row' => 'Ð\98зÑ\82Ñ\80иване Ð½Ð° Ñ\80ед',
     'insert_column_before' => 'Вмъкни колоната преди',
     'insert_column_after' => 'Вмъкни колоната след',
     'delete_column' => 'Изтрий колоната',
     'table_cell' => 'Клетка',
     'table_row' => 'Ред',
     'table_column' => 'Колона',
-    'cell_properties' => 'Ð\9dаÑ\81Ñ\82Ñ\80ойки на клетката',
-    'cell_properties_title' => 'Ð\9dаÑ\81Ñ\82Ñ\80ойки на клетката',
+    'cell_properties' => 'СвойÑ\81Ñ\82ва на клетката',
+    'cell_properties_title' => 'СвойÑ\81Ñ\82ва на клетката',
     'cell_type' => 'Тип на клетката',
     'cell_type_cell' => 'Клетка',
     'cell_scope' => 'Scope',
@@ -115,7 +115,7 @@ return [
     'row_type_header' => 'Заглавка',
     'row_type_body' => 'Тяло',
     'row_type_footer' => 'Долна част',
-    'alignment' => 'Разположение',
+    'alignment' => 'Ð\9fодÑ\80авнÑ\8fване',
     'cut_column' => 'Изрежи колоната',
     'copy_column' => 'Копирай колоната',
     'paste_column_before' => 'Постави колоната преди',
@@ -148,7 +148,7 @@ return [
     'open_link_in' => 'Open link in...',
     'open_link_current' => 'Текущ прозорец',
     'open_link_new' => 'Нов прозорец',
-    'remove_link' => 'Remove link',
+    'remove_link' => 'Премахване на връзка',
     'insert_collapsible' => 'Вмъкни сгъваем блок',
     'collapsible_unwrap' => 'Разгъни',
     'edit_label' => 'Редактирай етикета',
@@ -157,10 +157,10 @@ return [
     'toggle_label' => 'Превключи надписа',
 
     // About view
-    'about' => 'About the editor',
+    'about' => 'За редактора',
     'about_title' => 'Относно визуалния редактор',
     'editor_license' => 'Лиценз, авторски и сходни права на редактора',
-    'editor_tiny_license' => 'This editor is built using :tinyLink which is provided under the MIT license.',
+    'editor_tiny_license' => 'Този редактор е изграден посредством :tinyLink, което е предоставен под лиценз MIT.',
     'editor_tiny_license_link' => 'Авторското и сходните му права, както и лицензът на TinyMCE, могат да бъдат намерени тук.',
     'save_continue' => 'Запази страницата и продължи',
     'callouts_cycle' => '(Продължавай да натискаш, за да превключваш типовете)',
@@ -168,7 +168,7 @@ return [
     'shortcuts' => 'Преки пътища',
     'shortcut' => 'Пряк път',
     'shortcuts_intro' => 'Следните клавишни комбинации са налични за редактора:',
-    'windows_linux' => '(Windows/Linux)',
-    'mac' => '(Mac)',
+    'windows_linux' => '(Уиндоус/Линукс)',
+    'mac' => '(Мак.)',
     'description' => 'Описание',
 ];
index 5d927ea5ea0c03d3f38c0e6068ffd2863f300081..fe83dabe1ecdcd4370495598c64c38a16398e853 100644 (file)
@@ -61,7 +61,7 @@ return [
 
     // Entities
     'entity_not_found' => 'Обектът не е намерен',
-    'bookshelf_not_found' => 'Shelf not found',
+    'bookshelf_not_found' => 'Няма намерен рафт',
     'book_not_found' => 'Книгата не е намерена',
     'page_not_found' => 'Страницата не е намерена',
     'chapter_not_found' => 'Главата не е намерена',
index e9a47461b3d18a42a68699d729acf99b07b9eda5..b38f35e48e1f7b4a94e2957e8b1a8f6bb68c2c07 100644 (file)
@@ -5,14 +5,14 @@
  */
 
 return [
-    'shortcuts' => 'Shortcuts',
+    'shortcuts' => 'Преки пътища',
     'shortcuts_interface' => 'Interface Keyboard Shortcuts',
     'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.',
     'shortcuts_customize_desc' => 'You can customize each of the shortcuts below. Just press your desired key combination after selecting the input for a shortcut.',
     'shortcuts_toggle_label' => 'Keyboard shortcuts enabled',
-    'shortcuts_section_navigation' => 'Navigation',
+    'shortcuts_section_navigation' => 'Навигация',
     'shortcuts_section_actions' => 'Common Actions',
-    'shortcuts_save' => 'Save Shortcuts',
+    'shortcuts_save' => 'Запазване на преките пътища',
     'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.',
-    'shortcuts_update_success' => 'Shortcut preferences have been updated!',
+    'shortcuts_update_success' => 'Обновени предпочитания за преки пътища!',
 ];
\ No newline at end of file
index a8ded98258465e47f118535d822dd354d1bd9c0f..94bc5a6176bff07d71469782d5e60fd40a85eb57 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index 76e689b6d583b6ab2c02ab2c41ca4ff034b591e0..38d817915b82ed3129d80d4f213a3444d8130af7 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index 7a3a611a818bea62336784137402d3ec9ee2afa2..2640198c0decd789df437df464405c6a1cb443e9 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index ac57d4e7274748120e125b57fdd898a8c0b227e8..50c17e39bf632136abc114dfab29153c8f73be7f 100644 (file)
@@ -68,9 +68,9 @@ return [
     'user_delete_notification' => 'Uživatel byl úspěšně odstraněn',
 
     // Roles
-    'role_create_notification' => 'Role successfully created',
-    'role_update_notification' => 'Role successfully updated',
-    'role_delete_notification' => 'Role successfully deleted',
+    'role_create_notification' => 'Role byla úspěšně vytvořena',
+    'role_update_notification' => 'Role byla úspěšně aktualizována',
+    'role_delete_notification' => 'Role byla odstraněna',
 
     // Other
     'commented_on'                => 'okomentoval/a',
index 7134635628f172d746a86311f4d783328af3229b..ac5694231d36c93085fb4ecf08af558fab6bf2ac 100644 (file)
@@ -23,7 +23,7 @@ return [
     'meta_updated' => 'Aktualizováno :timeLength',
     'meta_updated_name' => 'Aktualizováno :timeLength uživatelem :user',
     'meta_owned_name' => 'Vlastník :user',
-    'meta_reference_page_count' => 'Referenced on :count page|Referenced on :count pages',
+    'meta_reference_page_count' => 'Odkazováno na 1 stránce|Odkazováno na :count stranách',
     'entity_select' => 'Výběr entity',
     'entity_select_lack_permission' => 'Nemáte dostatečná oprávnění k výběru této položky',
     'images' => 'Obrázky',
index 11f2058f92569a7d741982f0b445e5827c1c4bda..35c49bf8502802e90f5474b2fb8004565ed05644 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Barevné schéma aplikace',
-    'color_scheme_desc' => 'Nastavte barvy pro použití v rozhraní BookStack. Barvy mohou být nastaveny samostatně pro tmavé a světlé režimy, aby se nejlépe vešly do motivu a zajistila čitelnost.',
-    'ui_colors_desc' => 'Nastavte primární barvu a výchozí barvu odkazů pro BookStack. Hlavní barva se používá hlavně pro banner hlavičky, tlačítka a dekorace rozhraní. Výchozí barva odkazu se používá pro textové odkazy a akce, a to jak v psaném obsahu, tak v rozhraní Bookstack.',
+    'color_scheme_desc' => 'Nastavte barvy pro uživatelské rozhraní. Barvy mohou být konfigurovány samostatně pro tmavý a světlý režim, aby co nejlépe odpovídaly tématu a zajistily čitelnost.',
+    'ui_colors_desc' => 'Nastavte primární barvu aplikace a výchozí barvu odkazu. Primární barva je použitá hlavně na banneru hlavičky, tlačítkách a ozdobách rozhraní. Výchozí barva odkazu se používá pro odkazy a akce napříč psaným textem a rozhraním aplikace.',
     'app_color' => 'Hlavní barva',
     'link_color' => 'Výchozí barva odkazu',
     'content_colors_desc' => 'Nastaví barvy pro všechny prvky v organizační struktuře stránky. Pro lepší čitelnost doporučujeme zvolit barvy s podobným jasem, jakou mají výchozí barvy.',
@@ -138,8 +138,8 @@ return [
     'roles' => 'Role',
     'role_user_roles' => 'Uživatelské role',
     'roles_index_desc' => 'Role se používají ke sdružování uživatelů a k poskytování systémových oprávnění jejich členům. Pokud je uživatel členem více rolí, udělená oprávnění budou uložena a uživatel zdědí všechny schopnosti.',
-    'roles_x_users_assigned' => ':count user assigned|:count users assigned',
-    'roles_x_permissions_provided' => ':count permission|:count permissions',
+    'roles_x_users_assigned' => '1 přiřazený uživatel|:count přiřazených uživatelů',
+    'roles_x_permissions_provided' => '1 oprávnění|:count oprávnění',
     'roles_assigned_users' => 'Přiřazení uživatelé',
     'roles_permissions_provided' => 'Poskytnutá oprávnění',
     'role_create' => 'Vytvořit novou roli',
@@ -249,7 +249,7 @@ return [
     // Webhooks
     'webhooks' => 'Webhooky',
     'webhooks_index_desc' => 'Webhooks jsou způsob, jak odeslat data na externí URL, pokud se vyskytnou určité akce a události v systému, které umožňují integraci událostí s externími platformami, jako jsou systémy zasílání zpráv nebo oznámení.',
-    'webhooks_x_trigger_events' => ':count trigger event|:count trigger events',
+    'webhooks_x_trigger_events' => '1 spouštěcí událost|:count spouštěcích událostí',
     'webhooks_create' => 'Vytvořit nový webhook',
     'webhooks_none_created' => 'Žádné webhooky nebyly doposud vytvořeny.',
     'webhooks_edit' => 'Upravit webhook',
index 76e689b6d583b6ab2c02ab2c41ca4ff034b591e0..38d817915b82ed3129d80d4f213a3444d8130af7 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index ea5533b6234eebe018de48fe9d3fc1c9bd335b5b..ecd12b34df8fb54a2336a779735a67f9ae128346 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index 08ee8fd3aa0927f7272928d01ce3d4a49f9c3d2f..d4346e27accdd1880c9a899b62dd5f4aad31fa41 100644 (file)
@@ -33,9 +33,9 @@ return [
     'app_custom_html_desc' => 'Jeder Inhalt, der hier hinzugefügt wird, wird am Ende der <head> Sektion jeder Seite eingefügt. Diese kann praktisch sein, um CSS Styles anzupassen oder Analytics-Code hinzuzufügen.',
     'app_custom_html_disabled_notice' => 'Benutzerdefinierte HTML-Kopfzeileninhalte sind auf dieser Einstellungsseite deaktiviert, um sicherzustellen, dass alle Änderungen rückgängig gemacht werden können.',
     'app_logo' => 'Anwendungslogo',
-    'app_logo_desc' => 'This is used in the application header bar, among other areas. This image should be 86px in height. Large images will be scaled down.',
-    'app_icon' => 'Application Icon',
-    'app_icon_desc' => 'This icon is used for browser tabs and shortcut icons. This should be a 256px square PNG image.',
+    'app_logo_desc' => 'Dies wird unter anderem in der Kopfzeile der Anwendung verwendet. Dieses Bild sollte 86px hoch sein. Große Bilder werden herunterskaliert.',
+    'app_icon' => 'Anwendungssymbol',
+    'app_icon_desc' => 'Dieses Symbol wird für Browser-Registerkarten und Verknüpfungssymbole verwendet. Dies sollte ein 256px quadratisches PNG-Bild sein.',
     'app_homepage' => 'Startseite der Anwendung',
     'app_homepage_desc' => 'Wählen Sie eine Seite als Startseite aus, die statt der Standardansicht angezeigt werden soll. Seitenberechtigungen werden für die ausgewählten Seiten ignoriert.',
     'app_homepage_select' => 'Wählen Sie eine Seite aus',
@@ -49,12 +49,12 @@ return [
     'app_disable_comments_desc' => 'Deaktiviert Kommentare über alle Seiten in der Anwendung. Vorhandene Kommentare werden nicht angezeigt.',
 
     // Color settings
-    'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
-    'app_color' => 'Primary Color',
-    'link_color' => 'Default Link Color',
-    'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
+    'color_scheme' => 'Farbschema der Anwendung',
+    'color_scheme_desc' => 'Lege die Farben, die in der Benutzeroberfläche verwendet werden, fest. Farben können separat für dunkle und helle Modi konfiguriert werden, um am besten zum Farbschema zu passen und die Lesbarkeit zu gewährleisten.',
+    'ui_colors_desc' => 'Lege die primäre Farbe und die Standard-Linkfarbe der Anwendung fest. Die primäre Farbe wird hauptsächlich für Kopfzeilen, Buttons und Interface-Dekorationen verwendet. Die Standard-Linkfarbe wird für textbasierte Links und Aktionen sowohl innerhalb des geschriebenen Inhalts als auch in der Benutzeroberfläche verwendet.',
+    'app_color' => 'Primäre Farbe',
+    'link_color' => 'Standard-Linkfarbe',
+    'content_colors_desc' => 'Legt Farben für alle Elemente in der Seitenorganisationshierarchie fest. Die Auswahl von Farben mit einer ähnlichen Helligkeit wie die Standardfarben wird zur Lesbarkeit empfohlen.',
     'bookshelf_color' => 'Regalfarbe',
     'book_color' => 'Buchfarbe',
     'chapter_color' => 'Kapitelfarbe',
@@ -139,8 +139,8 @@ Hinweis: Benutzer können ihre E-Mail-Adresse nach erfolgreicher Registrierung 
     'roles' => 'Rollen',
     'role_user_roles' => 'Benutzer-Rollen',
     'roles_index_desc' => 'Rollen werden verwendet, um Benutzer zu gruppieren System-Berechtigung für ihre Mitglieder zuzuweisen. Wenn ein Benutzer Mitglied mehrerer Rollen ist, stapeln die gewährten Berechtigungen und der Benutzer wird alle Fähigkeiten erben.',
-    'roles_x_users_assigned' => ':count user assigned|:count users assigned',
-    'roles_x_permissions_provided' => ':count permission|:count permissions',
+    'roles_x_users_assigned' => ':count Benutzer zugewiesen|:count Benutzer zugewiesen',
+    'roles_x_permissions_provided' => ':count Berechtigung|:count Berechtigungen',
     'roles_assigned_users' => 'Zugewiesene Benutzer',
     'roles_permissions_provided' => 'Genutzte Berechtigungen',
     'role_create' => 'Neue Rolle anlegen',
@@ -250,7 +250,7 @@ Hinweis: Benutzer können ihre E-Mail-Adresse nach erfolgreicher Registrierung 
     // Webhooks
     'webhooks' => 'Webhooks',
     'webhooks_index_desc' => 'Webhooks sind eine Möglichkeit, Daten an externe URLs zu senden, wenn bestimmte Aktionen und Ereignisse im System auftreten, was eine ereignisbasierte Integration mit externen Plattformen wie Messaging- oder Benachrichtigungssystemen ermöglicht.',
-    'webhooks_x_trigger_events' => ':count trigger event|:count trigger events',
+    'webhooks_x_trigger_events' => ':count Auslöserereignis|:count Auslöserereignisse',
     'webhooks_create' => 'Neuen Webhook erstellen',
     'webhooks_none_created' => 'Es wurden noch keine Webhooks erstellt.',
     'webhooks_edit' => 'Webhook bearbeiten',
index 05ce9ee3c370c8d0fa6b266ff06c269ec2b4f770..01e20f2c9f8f8a988187cf4e740c822f86a57c27 100644 (file)
@@ -68,7 +68,7 @@ return [
     'user_delete_notification' => 'Benutzer erfolgreich entfernt',
 
     // Roles
-    'role_create_notification' => 'Rolle erfolgreich angelegt',
+    'role_create_notification' => 'Rolle erfolgreich erstellt',
     'role_update_notification' => 'Rolle erfolgreich aktualisiert',
     'role_delete_notification' => 'Rolle erfolgreich gelöscht',
 
index 8f9ce1c151285cbe0a2083a38cb3715cc84d72ad..dcefac0d1abf519586f6d09d1c4a55f814777b53 100644 (file)
@@ -141,7 +141,7 @@ return [
     'books_search_this' => 'Dieses Buch durchsuchen',
     'books_navigation' => 'Buchnavigation',
     'books_sort' => 'Buchinhalte sortieren',
-    'books_sort_desc' => 'Kapitel und Seiten innerhalb eines Buches verschieben, um dessen Inhalt zu reorganisieren. Andere Bücher können hinzugefügt werden, was das Verschieben von Kapiteln und Seiten zwischen Büchern erleichtert.',
+    'books_sort_desc' => 'Kapitel und Seiten innerhalb eines Buches verschieben, um dessen Inhalt neu zu ordnen. Andere Bücher können hinzugefügt werden, was das Verschieben von Kapiteln und Seiten zwischen Büchern erleichtert.',
     'books_sort_named' => 'Buch ":bookName" sortieren',
     'books_sort_name' => 'Sortieren nach Namen',
     'books_sort_created' => 'Sortieren nach Erstellungsdatum',
@@ -150,15 +150,15 @@ return [
     'books_sort_chapters_last' => 'Kapitel zuletzt',
     'books_sort_show_other' => 'Andere Bücher anzeigen',
     'books_sort_save' => 'Neue Reihenfolge speichern',
-    'books_sort_show_other_desc' => 'Füge hier weitere Bücher hinzu, um sie in die Sortierung einzubinden und ermögliche so eine einfache und übergreifende Reorganisation.',
+    'books_sort_show_other_desc' => 'Füge hier weitere Bücher hinzu, um sie in die Sortierung einzubinden und ermögliche so eine einfache und übergreifende Neuordnung.',
     'books_sort_move_up' => 'Nach oben bewegen',
     'books_sort_move_down' => 'Nach unten bewegen',
     'books_sort_move_prev_book' => 'Zum vorherigen Buch verschieben',
     'books_sort_move_next_book' => 'Zum nächsten Buch verschieben',
-    'books_sort_move_prev_chapter' => 'In das vorherige Kapitel verschieben',
-    'books_sort_move_next_chapter' => 'In nächstes Kapitel verschieben',
+    'books_sort_move_prev_chapter' => 'Ins vorherige Kapitel verschieben',
+    'books_sort_move_next_chapter' => 'Ins nächste Kapitel verschieben',
     'books_sort_move_book_start' => 'Zum Buchbeginn verschieben',
-    'books_sort_move_book_end' => 'Zum Ende des Buches verschieben',
+    'books_sort_move_book_end' => 'Zum Buchende verschieben',
     'books_sort_move_before_chapter' => 'Vor Kapitel verschieben',
     'books_sort_move_after_chapter' => 'Nach Kapitel verschieben',
     'books_copy' => 'Buch kopieren',
index d7222bd33e2f2aec5cac9e09205a92e33391739f..af38663ae3af5cc45f8e2d4426e72567e6c83a41 100644 (file)
@@ -50,7 +50,7 @@ return [
     // Drawing & Images
     'image_upload_error' => 'Beim Hochladen des Bildes trat ein Fehler auf.',
     'image_upload_type_error' => 'Der Bildtyp der hochgeladenen Datei ist ungültig.',
-    'drawing_data_not_found' => 'Zeichnungsdaten konnten nicht geladen werden. Die Zeichnungsdatei existiert möglicherweise nicht mehr oder Sie haben nicht die Berechtigung, darauf zuzugreifen.',
+    'drawing_data_not_found' => 'Zeichnungsdaten konnten nicht geladen werden. Die Zeichnungsdatei existiert möglicherweise nicht mehr oder du hast nicht die Berechtigung, darauf zuzugreifen.',
 
     // Attachments
     'attachment_not_found' => 'Anhang konnte nicht gefunden werden.',
index 9ff7605e08c98f9b9732bffdbb10089d16f53b53..4ab1784042ec721ce502cf8c37c82afcf9824408 100644 (file)
@@ -33,9 +33,9 @@ return [
     'app_custom_html_desc' => 'Jeder Inhalt, der hier hinzugefügt wird, wird am Ende der <head> Sektion jeder Seite eingefügt. Diese kann praktisch sein, um CSS Styles anzupassen oder Analytics-Code hinzuzufügen.',
     'app_custom_html_disabled_notice' => 'Benutzerdefinierte HTML-Kopfzeileninhalte sind auf dieser Einstellungsseite deaktiviert, um sicherzustellen, dass alle Änderungen rückgängig gemacht werden können.',
     'app_logo' => 'Anwendungslogo',
-    'app_logo_desc' => 'This is used in the application header bar, among other areas. This image should be 86px in height. Large images will be scaled down.',
-    'app_icon' => 'Application Icon',
-    'app_icon_desc' => 'This icon is used for browser tabs and shortcut icons. This should be a 256px square PNG image.',
+    'app_logo_desc' => 'Dies wird unter anderem in der Kopfzeile der Anwendung verwendet. Dieses Bild sollte 86px hoch sein. Große Bilder werden herunterskaliert.',
+    'app_icon' => 'Anwendungssymbol',
+    'app_icon_desc' => 'Dieses Symbol wird für Browser-Registerkarten und Verknüpfungssymbole verwendet. Dies sollte ein 256px quadratisches PNG-Bild sein.',
     'app_homepage' => 'Startseite der Anwendung',
     'app_homepage_desc' => 'Wähle eine Seite als Startseite aus, die statt der Standardansicht angezeigt werden soll. Seitenberechtigungen werden für die ausgewählten Seiten ignoriert.',
     'app_homepage_select' => 'Wähle eine Seite aus',
@@ -49,12 +49,12 @@ return [
     'app_disable_comments_desc' => 'Deaktiviert Kommentare über alle Seiten in der Anwendung. Vorhandene Kommentare werden nicht angezeigt.',
 
     // Color settings
-    'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
-    'app_color' => 'Primary Color',
-    'link_color' => 'Default Link Color',
-    'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
+    'color_scheme' => 'Farbschema der Anwendung',
+    'color_scheme_desc' => 'Lege die Farben, die in der Benutzeroberfläche verwendet werden, fest. Farben können separat für dunkle und helle Modi konfiguriert werden, um am besten zum Farbschema zu passen und die Lesbarkeit zu gewährleisten.',
+    'ui_colors_desc' => 'Lege die primäre Farbe und die Standard-Linkfarbe der Anwendung fest. Die primäre Farbe wird hauptsächlich für Kopfzeilen, Buttons und Interface-Dekorationen verwendet. Die Standard-Linkfarbe wird für textbasierte Links und Aktionen sowohl innerhalb des geschriebenen Inhalts als auch in der Benutzeroberfläche verwendet.',
+    'app_color' => 'Primäre Farbe',
+    'link_color' => 'Standard-Linkfarbe',
+    'content_colors_desc' => 'Lege Farben für alle Elemente in der Seitenorganisationshierarchie fest. Die Auswahl von Farben mit einer ähnlichen Helligkeit wie die Standardfarben wird zur Lesbarkeit empfohlen.',
     'bookshelf_color' => 'Regalfarbe',
     'book_color' => 'Buchfarbe',
     'chapter_color' => 'Kapitelfarbe',
@@ -139,8 +139,8 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
     'roles' => 'Rollen',
     'role_user_roles' => 'Benutzer-Rollen',
     'roles_index_desc' => 'Rollen werden verwendet, um Benutzer zu gruppieren und System-Berechtigungen für ihre Mitglieder zuzuweisen. Wenn ein Benutzer Mitglied mehrerer Rollen ist, stapeln die gewährten Berechtigungen und der Benutzer wird alle Fähigkeiten erben.',
-    'roles_x_users_assigned' => ':count user assigned|:count users assigned',
-    'roles_x_permissions_provided' => ':count permission|:count permissions',
+    'roles_x_users_assigned' => ':count Benutzer zugewiesen|:count Benutzer zugewiesen',
+    'roles_x_permissions_provided' => ':count Berechtigung|:count Berechtigungen',
     'roles_assigned_users' => 'Zugewiesene Benutzer',
     'roles_permissions_provided' => 'Genutzte Berechtigungen',
     'role_create' => 'Neue Rolle anlegen',
@@ -250,7 +250,7 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
     // Webhooks
     'webhooks' => 'Webhooks',
     'webhooks_index_desc' => 'Webhooks sind eine Möglichkeit, Daten an externe URLs zu senden, wenn bestimmte Aktionen und Ereignisse im System auftreten, was eine ereignisbasierte Integration mit externen Plattformen wie Messaging- oder Benachrichtigungssystemen ermöglicht.',
-    'webhooks_x_trigger_events' => ':count trigger event|:count trigger events',
+    'webhooks_x_trigger_events' => ':count Auslöserereignis|:count Auslöserereignisse',
     'webhooks_create' => 'Neuen Webhook erstellen',
     'webhooks_none_created' => 'Es wurden noch keine Webhooks erstellt.',
     'webhooks_edit' => 'Webhook bearbeiten',
index 0be72fa71d09fdbf3e6fc338dbb69a8eee0495cc..86671ca8c3b74df27aa3d96140b10f60d1374b31 100644 (file)
@@ -68,9 +68,9 @@ return [
     'user_delete_notification' => 'Ο Χρήστης αφαιρέθηκε επιτυχώς',
 
     // Roles
-    'role_create_notification' => 'Role successfully created',
-    'role_update_notification' => 'Role successfully updated',
-    'role_delete_notification' => 'Role successfully deleted',
+    'role_create_notification' => 'Ο Ρόλος δημιουργήθηκε με επιτυχία',
+    'role_update_notification' => 'Ο Ρόλος ενημερώθηκε με επιτυχία',
+    'role_delete_notification' => 'Ο Ρόλος διαγράφηκε επιτυχώς',
 
     // Other
     'commented_on'                => 'σχολίασε',
index 04cfd817fb62d45a5c0ba56fa7ac5bf84a9415b3..6904bced44880d35d899e52eab646ec9c7421de0 100644 (file)
@@ -19,11 +19,11 @@ return [
     'revisions' => 'Αναθεωρήσεις',
     'meta_revision' => 'Αναθεώρηση #:revisionCount',
     'meta_created' => 'Δημιουργήθηκε :timeLength',
-    'meta_created_name' => 'Δημιουργήθηκε :timeLength by :user',
+    'meta_created_name' => 'Δημιουργήθηκε :timeLength από :user',
     'meta_updated' => 'Ενημερώθηκε :timeLength',
-    'meta_updated_name' => 'Ενημερώθηκε :timeLength by :user',
+    'meta_updated_name' => 'Ενημερώθηκε :timeLength από :user',
     'meta_owned_name' => 'Ανήκει στον :user',
-    'meta_reference_page_count' => 'Referenced on :count page|Referenced on :count pages',
+    'meta_reference_page_count' => 'Αναφορά σε :count σελίδας|Αναφορά στο :count σελίδες',
     'entity_select' => 'Επιλογή Οντότητας',
     'entity_select_lack_permission' => 'Δεν έχετε τα απαιτούμενα δικαιώματα για να επιλέξετε αυτό το στοιχείο',
     'images' => 'Εικόνες',
@@ -43,18 +43,18 @@ return [
     // Permissions and restrictions
     'permissions' => 'Δικαιώματα',
     'permissions_desc' => 'Ορίστε εδώ δικαιώματα για να παρακάμψετε τα προκαθορισμένα δικαιώματα που παρέχονται από τους ρόλους των χρηστών.',
-    'permissions_book_cascade' => 'Permissions set on books will automatically cascade to child chapters and pages, unless they have their own permissions defined.',
-    'permissions_chapter_cascade' => 'Permissions set on chapters will automatically cascade to child pages, unless they have their own permissions defined.',
+    'permissions_book_cascade' => 'Τα δικαιώματα που έχουν οριστεί στα Βιβλία θα κλιμακώνονται αυτόματα στα θυγατρικά Κεφάλαια και Σελίδες, εκτός εάν έχουν καθοριστεί διαφορετικά σε αυτά.',
+    'permissions_chapter_cascade' => 'Τα δικαιώματα που έχουν οριστεί στα Κεφάλαια θα κλιμακώνονται αυτόματα στις θυγατρικές Σελίδες, εκτός εάν έχουν καθοριστεί διαφορετικά σε αυτές.',
     'permissions_save' => 'Αποθήκευση Δικαιωμάτων',
     'permissions_owner' => 'Ιδιοκτήτης / Κάτοχος',
-    'permissions_role_everyone_else' => 'Everyone Else',
+    'permissions_role_everyone_else' => 'Όλοι Οι Άλλοι',
     'permissions_role_everyone_else_desc' => 'Ορίστε δικαιώματα για όλους τους ρόλους που δεν παραβλέπονται συγκεκριμένα.',
     'permissions_role_override' => 'Παράκαμψη δικαιωμάτων για ρόλο',
     'permissions_inherit_defaults' => 'Κληρονόμηση προεπιλογών',
 
     // Search
     'search_results' => 'Αποτελέσματα αναζήτησης',
-    'search_total_results_found' => ':count αποτέλεσμα που βρέθηκε:count συνολικά αποτελέσματα που βρέθηκαν',
+    'search_total_results_found' => 'Βρέθηκε :count αποτέλεσμα|Βρέθηκαν συνολικά :count αποτελέσματα',
     'search_clear' => 'Καθαρισμός αναζήτησης',
     'search_no_pages' => 'Καμία σελίδα δεν ταιριάζει με αυτήν την αναζήτηση',
     'search_for_term' => 'Αναζήτηση για :term',
@@ -65,7 +65,7 @@ return [
     'search_exact_matches' => 'Ακριβείς αντιστοιχίες',
     'search_tags' => 'Αναζητήσεις Ετικετών',
     'search_options' => 'Επιλογές',
-    'search_viewed_by_me' => 'Προβλήθηκε από μένα',
+    'search_viewed_by_me' => 'ΠÏ\81οβλήθηκε Î±Ï\80Ï\8c ÎµÎ¼Î­Î½Î±',
     'search_not_viewed_by_me' => 'Δεν προβλήθηκε από εμένα',
     'search_permissions_set' => 'Τα δικαιώματα ορίστηκαν',
     'search_created_by_me' => 'Δημιουργήθηκε από εμένα',
@@ -141,7 +141,7 @@ return [
     'books_search_this' => 'Αναζήτηση σε αυτό το βιβλίο',
     'books_navigation' => 'Πλοήγηση Βιβλίου',
     'books_sort' => 'Ταξινόμηση Περιεχομένων Βιβλίου',
-    'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.',
+    'books_sort_desc' => 'Μετακινήστε κεφάλαια και σελίδες μέσα σε ένα βιβλίο για να αναδιοργανώσετε τα περιεχόμενά του. Μπορούν να προστεθούν και άλλα βιβλία, και επιτρέπουν την εύκολη μετακίνηση κεφαλαίων και σελίδων μεταξύ βιβλίων.',
     'books_sort_named' => 'Ταξινόμηση Βιβλίου :bookname',
     'books_sort_name' => 'Ταξινόμηση κατά όνομα',
     'books_sort_created' => 'Ταξινόμηση κατά ημερομηνία δημιουργίας',
@@ -150,24 +150,24 @@ return [
     'books_sort_chapters_last' => 'Τελευταία Κεφάλαια',
     'books_sort_show_other' => 'Εμφάνιση Άλλων Βιβλίων',
     'books_sort_save' => 'Αποθήκευση Νέας Ταξινόμησης',
-    'books_sort_show_other_desc' => 'Add other books here to include them in the sort operation, and allow easy cross-book reorganisation.',
-    'books_sort_move_up' => 'Move Up',
-    'books_sort_move_down' => 'Move Down',
-    'books_sort_move_prev_book' => 'Move to Previous Book',
-    'books_sort_move_next_book' => 'Move to Next Book',
-    'books_sort_move_prev_chapter' => 'Move Into Previous Chapter',
-    'books_sort_move_next_chapter' => 'Move Into Next Chapter',
-    'books_sort_move_book_start' => 'Move to Start of Book',
-    'books_sort_move_book_end' => 'Move to End of Book',
-    'books_sort_move_before_chapter' => 'Move to Before Chapter',
-    'books_sort_move_after_chapter' => 'Move to After Chapter',
+    'books_sort_show_other_desc' => 'Προσθέστε άλλα βιβλία εδώ για να τα συμπεριλάβετε στην ταξινόμηση και να επιτρέψετε την εύκολη αναδιοργάνωση μεταξύ των βιβλίων.',
+    'books_sort_move_up' => 'Μετακίνηση προς τα Επάνω',
+    'books_sort_move_down' => 'Μετακίνηση προς τα Κάτω',
+    'books_sort_move_prev_book' => 'Μετακίνηση στο προηγούμενο Βιβλίο',
+    'books_sort_move_next_book' => 'Μετακίνηση στο επόμενο Βιβλίο',
+    'books_sort_move_prev_chapter' => 'Μετακίνηση στο προηγούμενο Κεφάλαιο',
+    'books_sort_move_next_chapter' => 'Μετακίνηση στο επόμενο Κεφάλαιο',
+    'books_sort_move_book_start' => 'Μετακίνηση στην Αρχή του Βιβλίου',
+    'books_sort_move_book_end' => 'Μετακίνηση στο Τέλος του Βιβλίου',
+    'books_sort_move_before_chapter' => 'Μετακίνηση στο προηγούμενο Κεφάλαιο',
+    'books_sort_move_after_chapter' => 'Μετακίνηση στο επόμενο Κεφάλαιο',
     'books_copy' => 'Αντιγραφή Βιβλίου',
     'books_copy_success' => 'Το βιβλίο αντιγράφηκε επιτυχώς',
 
     // Chapters
     'chapter' => 'Κεφάλαιο',
     'chapters' => 'Κεφάλαια',
-    'x_chapters' => ':count Κεφάλαιο:count Κεφάλαια',
+    'x_chapters' => ':count Κεφάλαιο|:count Κεφάλαια',
     'chapters_popular' => 'Δημοφιλή Κεφάλαια',
     'chapters_new' => 'Νέο Κεφάλαιο',
     'chapters_create' => 'Δημιουργία Νέου Κεφαλαίου',
@@ -193,7 +193,7 @@ return [
     // Pages
     'page' => 'Σελίδα',
     'pages' => 'Σελίδες',
-    'x_pages' => ':count Σελίδα:count Σελίδες',
+    'x_pages' => ':count Σελίδα|:count Σελίδες',
     'pages_popular' => 'Δημοφιλείς Σελίδες',
     'pages_new' => 'Νέα Σελίδα',
     'pages_attachments' => 'Συνημμένα',
@@ -248,7 +248,7 @@ return [
     'pages_permissions_success' => 'Τα δικαιώματα σελίδας ενημερώθηκαν',
     'pages_revision' => 'Αναθεώρηση',
     'pages_revisions' => 'Αναθεωρήσεις Σελίδας',
-    'pages_revisions_desc' => 'Listed below are all the past revisions of this page. You can look back upon, compare, and restore old page versions if permissions allow. The full history of the page may not be fully reflected here since, depending on system configuration, old revisions could be auto-deleted.',
+    'pages_revisions_desc' => 'Παρακάτω αναφέρονται όλες οι προηγούμενες αναθεωρήσεις αυτής της Σελίδας. Μπορείτε να αναζητήσετε αντίγραφα ασφαλείας, να συγκρίνετε και να επαναφέρετε παλιές εκδόσεις Σελίδας, εάν τα δικαιώματα το επιτρέπουν. Το πλήρες ιστορικό της Σελίδας μπορεί να μην αντανακλάται πλήρως εδώ επειδή, ανάλογα με τη διαμόρφωση του συστήματος, οι παλιές αναθεωρήσεις θα μπορούσαν να διαγραφούν αυτόματα.',
     'pages_revisions_named' => 'Αναθεωρήσεις σελίδας για :pageName',
     'pages_revision_named' => 'Αναθεώρηση σελίδας για :pageName',
     'pages_revision_restored_from' => 'Επαναφορά από #:id; :summary',
@@ -292,7 +292,7 @@ return [
     'shelf_tags' => 'Ετικέτες Ραφιών',
     'tag' => 'Ετικέτα',
     'tags' =>  'Ετικέτες',
-    'tags_index_desc' => 'Tags can be applied to content within the system to apply a flexible form of categorization. Tags can have both a key and value, with the value being optional. Once applied, content can then be queried using the tag name and value.',
+    'tags_index_desc' => 'Οι Ετικέτες μπορούν να εφαρμοστούν στο περιεχόμενο μέσα στο σύστημα για να εφαρμοστεί μια ευέλικτη μορφή κατηγοριοποίησης. Οι Ετικέτες μπορούν να έχουν τόσο κλειδί όσο και αξία, με την τιμή να είναι προαιρετική. Μόλις εφαρμοστεί, μπορεί να παρθεί περιεχόμενο χρησιμοποιώντας το όνομα της Ετικέτας και την τιμή.',
     'tag_name' =>  'Όνομα Ετικέτας',
     'tag_value' => 'Τιμή Ετικέτας (Προαιρετικό)',
     'tags_explain' => "Προσθέστε μερικές ετικέτες για να κατηγοριοποιήσετε καλύτερα το περιεχόμενό σας. \n Μπορείτε να αντιστοιχίσετε μια τιμή σε μια ετικέτα για πιο αναλυτική οργάνωση.",
@@ -354,7 +354,7 @@ return [
     'comments' => 'Σχόλια',
     'comment_add' => 'Προσθήκη Σχολίου',
     'comment_placeholder' => 'Αφήστε ένα σχόλιο εδώ',
-    'comment_count' => '{0} Κανένα σχόλιο{1} 1 Σχόλιο [2,*] :count Σχόλια',
+    'comment_count' => '{0} Κανένα Σχόλιο |{1} 1 Σχόλιο |[2,*] :count Σχόλια',
     'comment_save' => 'Αποθήκευση Σχολίου',
     'comment_saving' => 'Αποθήκευση σχολίου...',
     'comment_deleting' => 'Διαγραφή σχολίου...',
index 86870cdecf5385147b39c40bdbfcfdfdb8fb7834..ea8807b58ad0923aec3794a08447a34914bcd27a 100644 (file)
@@ -50,7 +50,7 @@ return [
     // Drawing & Images
     'image_upload_error' => 'Παρουσιάστηκε σφάλμα κατά το ανέβασμα της εικόνας.',
     'image_upload_type_error' => 'Ο τύπος εικόνας που μεταφορτώθηκε δεν είναι έγκυρος',
-    'drawing_data_not_found' => 'Drawing data could not be loaded. The drawing file might no longer exist or you may not have permission to access it.',
+    'drawing_data_not_found' => 'Δεν ήταν δυνατή η φόρτωση δεδομένων σχεδίασης. Το αρχείο σχεδίασης ενδέχεται να μην υπάρχει πλέον ή ενδέχεται να μην έχετε άδεια πρόσβασης σε αυτά.',
 
     // Attachments
     'attachment_not_found' => 'Το συνημμένο δεν βρέθηκε',
index 922864ea988dec11542a29ed8c5207d121b407b4..3fe5c1fa71bc3140cdee027d3b7d77f5920c316b 100644 (file)
@@ -33,9 +33,9 @@ return [
     'app_custom_html_desc' => 'Οποιοδήποτε περιεχόμενο προστίθεται εδώ θα εισαχθεί στο κάτω μέρος της ενότητας <head> κάθε σελίδας. Αυτό είναι βολικό για την παράκαμψη ή προσθήκη στυλ καθώς και την προσθήκη κώδικα αναλυτικών στοιχείων.',
     'app_custom_html_disabled_notice' => 'Το προσαρμοσμένο περιεχόμενο κεφαλίδας HTML είναι απενεργοποιημένο σε αυτήν τη σελίδα ρυθμίσεων, για να διασφαλιστεί ότι τυχόν αλλαγές που θα πραγματοποιηθούν και θα προκαλέσουν δυσλειτουργία στην ιστοσελίδα σας, μπορούν να επαναφερθούν.',
     'app_logo' => 'Λογότυπο εφαρμογής',
-    'app_logo_desc' => 'This is used in the application header bar, among other areas. This image should be 86px in height. Large images will be scaled down.',
-    'app_icon' => 'Application Icon',
-    'app_icon_desc' => 'This icon is used for browser tabs and shortcut icons. This should be a 256px square PNG image.',
+    'app_logo_desc' => 'Αυτό χρησιμοποιείται στη γραμμή κεφαλίδας εφαρμογής, μεταξύ άλλων περιοχών. Αυτή η εικόνα θα πρέπει να είναι 86px σε ύψος. Οι μεγάλες εικόνες θα κλιμακωθούν.',
+    'app_icon' => 'Εικονίδιο Εφαρμογής',
+    'app_icon_desc' => 'Αυτό το εικονίδιο χρησιμοποιείται για τις καρτέλες περιηγητή και τα εικονίδια συντομεύσεων. Αυτό πρέπει να είναι μια τετράγωνη εικόνα 256px σε μορφή PNG.',
     'app_homepage' => 'Αρχική σελίδα εφαρμογής',
     'app_homepage_desc' => 'Επιλέξτε μια προβολή για εμφάνιση στην αρχική σελίδα αντί για την προεπιλεγμένη προβολή. Τα δικαιώματα σελίδων αγνοούνται για επιλεγμένες σελίδες.',
     'app_homepage_select' => 'Επιλέξτε μια σελίδα',
@@ -49,12 +49,12 @@ return [
     'app_disable_comments_desc' => 'Απενεργοποιεί τα σχόλια σε όλες τις σελίδες της εφαρμογής. <br> Τα υπάρχοντα σχόλια δεν εμφανίζονται.',
 
     // Color settings
-    'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
-    'app_color' => 'Primary Color',
-    'link_color' => 'Default Link Color',
-    'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
+    'color_scheme' => 'Θέμα Χρωμάτων Εφαρμογής',
+    'color_scheme_desc' => 'Ορίστε τα χρώματα που θα χρησιμοποιηθούν στο περιβάλλον χρήστη της εφαρμογής. Τα χρώματα μπορούν να ρυθμιστούν ξεχωριστά για τις λειτουργίες Σκούρο ή Λευκό, για να ταιριάζει καλύτερα στο θέμα και να εξασφαλίσει αναγνωσιμότητα.',
+    'ui_colors_desc' => 'Ορίστε το πρωτεύον χρώμα της εφαρμογής και το προεπιλεγμένο χρώμα συνδέσμου. Το πρωτεύον χρώμα χρησιμοποιείται κυρίως για την κεφαλίδα, τα κουμπιά και τις διακοσμήσεις διεπαφής. Το προεπιλεγμένο χρώμα συνδέσμου χρησιμοποιείται για συνδέσμους και ενέργειες που βασίζονται στο κείμενο, τόσο μέσα στο γραπτό περιεχόμενο όσο και στη διεπαφή της εφαρμογής.',
+    'app_color' => 'Κυρίως χρώμα',
+    'link_color' => 'Κυρίως χρώμα Συνδέσμου',
+    'content_colors_desc' => 'Ορίζει τα χρώματα για όλα τα στοιχεία στην ιεραρχία οργάνωσης της ιστοσελίδας. Συνιστάται η επιλογή χρωμάτων με παρόμοια φωτεινότητα με τα προεπιλεγμένα, για μέγιστη αναγνωσιμότητα.',
     'bookshelf_color' => 'Χρώμα Ραφιού',
     'book_color' => 'Χρώμα Βιβλίων',
     'chapter_color' => 'Χρώμα Κεφαλαίων Βιβλίων',
@@ -137,9 +137,9 @@ return [
     // Role Settings
     'roles' => 'Ρόλοι',
     'role_user_roles' => 'Ρόλοι Χρηστών',
-    'roles_index_desc' => 'Roles are used to group users & provide system permission to their members. When a user is a member of multiple roles the privileges granted will stack and the user will inherit all abilities.',
-    'roles_x_users_assigned' => ':count user assigned|:count users assigned',
-    'roles_x_permissions_provided' => ':count permission|:count permissions',
+    'roles_index_desc' => 'Οι Ρόλοι χρησιμοποιούνται για την ομαδοποίηση των χρηστών και παρέχουν δικαιώματα για το σύστημα στα μέλη τους. Όταν ένας χρήστης είναι μέλος πολλαπλών Ρόλων, ο χρήστης θα κληρονομεί όλες τις ιδιότητες από όλους τους Ρόλους που ανήκει.',
+    'roles_x_users_assigned' => ':count εκχωρημένος χρήστης|:count εκχωρημένοι χρήστες',
+    'roles_x_permissions_provided' => ':count άδεια|:count άδειες',
     'roles_assigned_users' => 'Εκχωρημένοι χρήστες',
     'roles_permissions_provided' => 'Παρεχόμενα Δικαιώματα',
     'role_create' => 'Δημιουργία νέου ρόλου',
@@ -178,7 +178,7 @@ return [
 
     // Users
     'users' => 'Χρήστες',
-    'users_index_desc' => 'Create & manage individual user accounts within the system. User accounts are used for login and attribution of content & activity. Access permissions are primarily role-based but user content ownership, among other factors, may also affect permissions & access.',
+    'users_index_desc' => 'Δημιουργία & διαχείριση μεμονωμένων λογαριασμών χρήστη μέσα στο σύστημα. Οι λογαριασμοί χρήστη χρησιμοποιούνται για τη σύνδεση και την απόδοση του περιεχομένου & δραστηριότητα. Τα δικαιώματα πρόσβασης βασίζονται κυρίως σε Ρόλους, αλλά η κυριότητα του περιεχομένου του χρήστη, μεταξύ άλλων παραγόντων, μπορεί επίσης να επηρεάσει τα δικαιώματα & την πρόσβαση.',
     'user_profile' => 'Προφίλ Χρήστη',
     'users_add_new' => 'Προσθήκη νέου Χρήστη',
     'users_search' => 'Αναζήτηση Χρηστών',
@@ -222,7 +222,7 @@ return [
     'users_api_tokens_docs' => 'Τεκμηρίωση API',
     'users_mfa' => 'Έλεγχος Ταυτοτητας Πολλαπλων Παραγοντων',
     'users_mfa_desc' => 'Ρυθμίστε τον έλεγχο ταυτότητας πολλαπλών παραγόντων ως ένα επιπλέον επίπεδο ασφάλειας για τον λογαριασμό χρήστη σας.',
-    'users_mfa_x_methods' => ':count μέθοδος έχει ρυθμιστεί:count μέθοδοι',
+    'users_mfa_x_methods' => 'Έχει ρυθμιστεί :count μέθοδος|Έχουν ρυθμιστεί :count μέθοδοι',
     'users_mfa_configure' => 'Ρύθμιση Μεθόδων',
 
     // API Tokens
@@ -248,8 +248,8 @@ return [
 
     // Webhooks
     'webhooks' => 'Webhooks',
-    'webhooks_index_desc' => 'Webhooks are a way to send data to external URLs when certain actions and events occur within the system which allows event-based integration with external platforms such as messaging or notification systems.',
-    'webhooks_x_trigger_events' => ':count trigger event|:count trigger events',
+    'webhooks_index_desc' => 'Τα Webhooks είναι ένας τρόπος αποστολής δεδομένων σε εξωτερικές διευθύνσεις URL όταν ορισμένες ενέργειες και συμβάντα συμβαίνουν στο σύστημα που επιτρέπει την ενσωμάτωση με εξωτερικές πλατφόρμες όπως συστήματα μηνυμάτων ή ειδοποιήσεων.',
+    'webhooks_x_trigger_events' => ':count συμβάν ενεργοποίησης|:count συμβάντα ενεργοποίησης',
     'webhooks_create' => 'Δημιουργία νέου Webhook',
     'webhooks_none_created' => 'Δεν έχουν δημιουργηθεί ακόμη webhook.',
     'webhooks_edit' => 'Επεξεργασία Webhook',
index d07d41dc17408873cadf042971bb6efb0721a797..d195863aed062b443a0804c77cc559541ce47833 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Esquema de color de la aplicación',
-    'color_scheme_desc' => 'Establecer los colores a usar en la interfaz de BookStack. Los colores pueden configurarse por separado para que los modos oscuros y claros se ajusten mejor al tema y garanticen la legibilidad.',
-    'ui_colors_desc' => 'Establezca el color principal y el color de los enlaces para BookStack. El color principal se utiliza principalmente para la cabecera, botones y decoraciones de la interfaz. El color de los enlaces se utiliza para enlaces y acciones de texto, tanto dentro del contenido escrito como en la interfaz de Bookstack.',
+    'color_scheme_desc' => 'Establece los colores a usar en la interfaz de BookStack. Los colores pueden configurarse por separado para que los modos oscuros y claros se ajusten mejor al tema y garanticen la legibilidad.',
+    'ui_colors_desc' => 'Establece el color principal y el color de los enlaces para BookStack. El color principal se utiliza principalmente para la cabecera, botones y decoraciones de la interfaz. El color de los enlaces se utiliza para enlaces y acciones de texto, tanto dentro del contenido escrito como en la interfaz de Bookstack.',
     'app_color' => 'Color principal',
     'link_color' => 'Color de enlaces por defecto',
     'content_colors_desc' => 'Establece los colores para todos los elementos en la jerarquía de la organización de la página. Se recomienda elegir colores con un brillo similar al predeterminado para mayor legibilidad.',
index 80acc34ab3fab1cfa9124bef75417db007a862ef..7fc214c1391976b86d4755c5c29b16231295c532 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Esquema de color de la aplicación',
-    'color_scheme_desc' => 'Establecer los colores a usar en la interfaz de BookStack. Los colores pueden configurarse por separado para que los modos oscuros y claros se ajusten mejor al tema y garanticen la legibilidad.',
-    'ui_colors_desc' => 'Establezca el color principal y el color de los enlaces para BookStack. El color principal se utiliza principalmente para la cabecera, botones y decoraciones de la interfaz. El color de los enlaces se utiliza para enlaces y acciones de texto, tanto dentro del contenido escrito como en la interfaz de Bookstack.',
+    'color_scheme_desc' => 'Establece los colores a usar en la interfaz de BookStack. Los colores pueden configurarse por separado para que los modos oscuros y claros se ajusten mejor al tema y garanticen la legibilidad.',
+    'ui_colors_desc' => 'Establece el color principal y el color de los enlaces para BookStack. El color principal se utiliza principalmente para la cabecera, botones y decoraciones de la interfaz. El color de los enlaces se utiliza para enlaces y acciones de texto, tanto dentro del contenido escrito como en la interfaz de Bookstack.',
     'app_color' => 'Color principal',
     'link_color' => 'Color de enlaces por defecto',
     'content_colors_desc' => 'Establece los colores para todos los elementos en la jerarquía de la organización de la página. Se recomienda elegir colores con un brillo similar al predeterminado para mayor legibilidad.',
index 0779f26e89a52949bdf0a7e190a7b6b4933fb362..ea93b64d89968198e7793ca7d30ebeca2686a33a 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Rakenduse värvid',
-    'color_scheme_desc' => 'Määra BookStack\'i kasutajaliidese värvid. Tumeda ja heleda režiimi värve saab sobivuse ja loetavuse huvides eraldi seadistada.',
-    'ui_colors_desc' => 'Määra BookStack\'i põhivärv ja vaikimisi linkide värv. Põhivärvi kasutatakse peamiselt päise, nuppude ning kasutajaliidese dekoratsioonide jaoks. Vaikimisi linkide värvi kasutatakse tekstipõhiste linkide ja tegevuste jaoks, nii kirjalikus sisus kui BookStack\'i kasutajaliideses.',
+    'color_scheme_desc' => 'Määra rakenduse kasutajaliidese värvid. Tumeda ja heleda režiimi värve saab sobivuse ja loetavuse huvides eraldi seadistada.',
+    'ui_colors_desc' => 'Määra rakenduse põhivärv ja vaikimisi linkide värv. Põhivärvi kasutatakse peamiselt päise, nuppude ning kasutajaliidese dekoratsioonide jaoks. Vaikimisi linkide värvi kasutatakse tekstipõhiste linkide ja tegevuste jaoks, nii kirjalikus sisus kui rakenduse kasutajaliideses.',
     'app_color' => 'Põhivärv',
     'link_color' => 'Vaikimisi linkide värv',
     'content_colors_desc' => 'Määra värvid erinevatele sisuelementidele. Loetavuse huvides on soovituslik valida värvid, mille heledus on sarnane vaikimisi värvidele.',
index d8d98d91702b160db14e8efb1a868f06d3a01141..260c8751615ee4da7fc365783700f2e2c9835e01 100644 (file)
@@ -42,14 +42,14 @@ return [
 
     // Permissions and restrictions
     'permissions' => 'Baimenak',
-    'permissions_desc' => 'Set permissions here to override the default permissions provided by user roles.',
+    'permissions_desc' => 'Ezarri baimenak hemen, erabiltzaileen rolek ematen dituzten baimenak gainidazteko.',
     'permissions_book_cascade' => 'Permissions set on books will automatically cascade to child chapters and pages, unless they have their own permissions defined.',
     'permissions_chapter_cascade' => 'Permissions set on chapters will automatically cascade to child pages, unless they have their own permissions defined.',
     'permissions_save' => 'Gorde baimenak',
     'permissions_owner' => 'Jabea',
     'permissions_role_everyone_else' => 'Everyone Else',
     'permissions_role_everyone_else_desc' => 'Set permissions for all roles not specifically overridden.',
-    'permissions_role_override' => 'Override permissions for role',
+    'permissions_role_override' => 'Gainidatzi baimenak rol honi',
     'permissions_inherit_defaults' => 'Inherit defaults',
 
     // Search
@@ -102,13 +102,13 @@ return [
     'shelves_delete_named' => 'Delete Shelf :name',
     'shelves_delete_explain' => "This will delete the shelf with the name ':name'. Contained books will not be deleted.",
     'shelves_delete_confirmation' => 'Are you sure you want to delete this shelf?',
-    'shelves_permissions' => 'Shelf Permissions',
-    'shelves_permissions_updated' => 'Shelf Permissions Updated',
-    'shelves_permissions_active' => 'Shelf Permissions Active',
-    'shelves_permissions_cascade_warning' => 'Permissions on shelves do not automatically cascade to contained books. This is because a book can exist on multiple shelves. Permissions can however be copied down to child books using the option found below.',
+    'shelves_permissions' => 'Apalategi baimenak',
+    'shelves_permissions_updated' => 'Apalategi baimenak eguneratuta',
+    'shelves_permissions_active' => 'Apalategi baimenak aktibatuta',
+    'shelves_permissions_cascade_warning' => 'Apaletako baimenak ez dira automatikoki hauen barneko liburuetan gordeko. Liburu bat apalategi askotan egon daitekeelako. Hala ere, baimenak apalategiko liburutara kopiatu daitezke, behean agertzen den aukera erabiliz.',
     'shelves_copy_permissions_to_books' => 'Kopiatu baimenak liburura',
     'shelves_copy_permissions' => 'Gorde baimenak',
-    'shelves_copy_permissions_explain' => 'This will apply the current permission settings of this shelf to all books contained within. Before activating, ensure any changes to the permissions of this shelf have been saved.',
+    'shelves_copy_permissions_explain' => 'Honek apalategi honen egungo baimen-konfigurazioa aplikatuko die barruan dauden liburu guztiei. Aktibatu aurretik, ziurtatu apaletan aldaketak gorde direla.',
     'shelves_copy_permission_success' => 'Shelf permissions copied to :count books',
 
     // Books
index cf471947d1a3b06f933e98002c8f660dea41168b..41f0331c1e2e8fbb1b4af51c5ab594f215ba3a34 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index 86daadcf3542d9dc03d485d76165b663ab4d1eff..2e61cce8dc01416ef25b4db9a31dc6e01b7cb0e7 100644 (file)
@@ -68,9 +68,9 @@ return [
     'user_delete_notification' => 'کاربر با موفقیت حذف شد',
 
     // Roles
-    'role_create_notification' => 'Role successfully created',
-    'role_update_notification' => 'Role successfully updated',
-    'role_delete_notification' => 'Role successfully deleted',
+    'role_create_notification' => 'نقش با موفقیت ایجاد شد',
+    'role_update_notification' => 'نقش با موفقیت به روز شد',
+    'role_delete_notification' => 'نقش با موفقیت حذف شد',
 
     // Other
     'commented_on'                => 'ثبت دیدگاه',
index 4de8b58002dd8c3f97e0ab4929aa97f619a8fcea..f4b30d4389284a1a48552080797a99fb71656852 100644 (file)
@@ -40,8 +40,8 @@ return [
 
     // Login auto-initiation
     'auto_init_starting' => 'تلاش برای ورود',
-    'auto_init_starting_desc' => 'We\'re contacting your authentication system to start the login process. If there\'s no progress after 5 seconds you can try clicking the link below.',
-    'auto_init_start_link' => 'Proceed with authentication',
+    'auto_init_starting_desc' => 'برای شروع فرآیند ورود به سیستم با سیستم احراز هویت شما تماس می گیریم. اگر بعد از 5 ثانیه پیشرفتی حاصل نشد، می توانید روی لینک زیر کلیک کنید.',
+    'auto_init_start_link' => 'احراز هویت را ادامه دهید',
 
     // Password Reset
     'reset_password' => 'بازنشانی کلمه عبور',
@@ -61,8 +61,8 @@ return [
     'email_confirm_send_error' => 'تایید پست الکترونیک الزامی می باشد، اما سیستم قادر به ارسال پیام نمی باشد.',
     'email_confirm_success' => 'ایمیل شما تایید شد! اکنون باید بتوانید با استفاده از این آدرس ایمیل وارد شوید.',
     'email_confirm_resent' => 'پیام تایید پست الکترونیک مجدد ارسال گردید، لطفا صندوق ورودی خود را بررسی نمایید.',
-    'email_confirm_thanks' => 'Thanks for confirming!',
-    'email_confirm_thanks_desc' => 'Please wait a moment while your confirmation is handled. If you are not redirected after 3 seconds press the "Continue" link below to proceed.',
+    'email_confirm_thanks' => 'تشکر بابت تایید!',
+    'email_confirm_thanks_desc' => 'لطفاً یک لحظه صبر کنید تا تأیید شما بررسی شود. اگر بعد از 3 ثانیه هدایت نشدید، بر روی لینک "ادامه" کلیک کنید تا ادامه دهید.',
 
     'email_not_confirmed' => 'پست الکترونیک تایید نشده است',
     'email_not_confirmed_text' => 'پست الکترونیک شما هنوز تایید نشده است.',
index bd6b95857ea14390ba202c0d97caaef96f3ff587..2cdca73420810ec7574b5b9a645c4ee17cc4ae15 100644 (file)
@@ -88,7 +88,7 @@ return [
     'edit_profile' => 'ویرایش پروفایل',
     'dark_mode' => 'حالت تاریک',
     'light_mode' => 'حالت روشن',
-    'global_search' => 'Global Search',
+    'global_search' => 'جستجوی سراسری',
 
     // Layout tabs
     'tab_info' => 'اطلاعات',
index 0109e6cad8e6dfe589aedae230c9a4cf4c222a08..3e0be53b85fb52171928fad039017abaf005be7e 100644 (file)
@@ -66,7 +66,7 @@ return [
     'insert_link_title' => 'افزودن/ویرایش پیوند',
     'insert_horizontal_line' => 'افزودن خط افقی',
     'insert_code_block' => 'افزودن بلوک کد',
-    'edit_code_block' => 'Edit code block',
+    'edit_code_block' => 'code block را ویرایش کنید',
     'insert_drawing' => 'افزودن/ویرایش طرح',
     'drawing_manager' => 'مدیریت طراحی',
     'insert_media' => 'افزودن/ویرایش رسانه',
@@ -165,7 +165,7 @@ return [
     'save_continue' => 'ذخیره صفحه و ادامه',
     'callouts_cycle' => '(جهت تغییر نوع ها چندین بار فشار دهید)',
     'link_selector' => 'پیوند به محتوا',
-    'shortcuts' => 'میانبرها',
+    'shortcuts' => 'کلیدهای میانبر',
     'shortcut' => 'میانبر',
     'shortcuts_intro' => 'میانبرهای قابل استفاده در این ویرایشگر:',
     'windows_linux' => '(ویندوز/لینوکس)',
index bc3961d4b4dad0f88e91be1e4b4f7621f6b4f042..186c2e418ed9843afc23cc80032bd511d97f4c9f 100644 (file)
@@ -22,7 +22,7 @@ return [
     'meta_created_name' => 'ایجاد شده :timeLength توسط :user',
     'meta_updated' => 'به روزرسانی شده :timeLength',
     'meta_updated_name' => 'به روزرسانی شده :timeLength توسط :user',
-    'meta_owned_name' => 'توسط :user ایجاد شده‌است',
+    'meta_owned_name' => 'متعلق به :user',
     'meta_reference_page_count' => 'Referenced on :count page|Referenced on :count pages',
     'entity_select' => 'انتخاب موجودیت',
     'entity_select_lack_permission' => 'شما مجوزهای لازم برای انتخاب این مورد را ندارید',
index e9a47461b3d18a42a68699d729acf99b07b9eda5..d0f1598e107b476577df8266eadd27e9605c7c39 100644 (file)
@@ -5,14 +5,14 @@
  */
 
 return [
-    'shortcuts' => 'Shortcuts',
-    'shortcuts_interface' => 'Interface Keyboard Shortcuts',
-    'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.',
-    'shortcuts_customize_desc' => 'You can customize each of the shortcuts below. Just press your desired key combination after selecting the input for a shortcut.',
-    'shortcuts_toggle_label' => 'Keyboard shortcuts enabled',
-    'shortcuts_section_navigation' => 'Navigation',
-    'shortcuts_section_actions' => 'Common Actions',
-    'shortcuts_save' => 'Save Shortcuts',
-    'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.',
-    'shortcuts_update_success' => 'Shortcut preferences have been updated!',
+    'shortcuts' => 'میانبرها',
+    'shortcuts_interface' => 'میانبرهای صفحه کلید',
+    'shortcuts_toggle_desc' => 'در اینجا می توانید میانبرهای سیستم را که برای پیمایش و ... استفاده می شود، فعال یا غیرفعال کنید.',
+    'shortcuts_customize_desc' => 'می توانید هر یک از میانبرهای زیر را سفارشی کنید. کافی است پس از انتخاب ورودی برای میانبر، کلید ترکیبی مورد نظر خود را فشار دهید.',
+    'shortcuts_toggle_label' => 'میانبرهای صفحه کلید فعال شد',
+    'shortcuts_section_navigation' => 'ناوبری و پیمایش',
+    'shortcuts_section_actions' => 'فعالیت/اقدامات مرسوم',
+    'shortcuts_save' => 'ذخیره کلیدهای میانبر',
+    'shortcuts_overlay_desc' => 'توجه: هنگامی که میانبرها فعال هستند، یک رابط کمکی با فشار دادن "؟" در دسترس است که میانبرهای موجود برای اقداماتی که در حال حاضر روی صفحه قابل مشاهده است را برجسته می‌کند.',
+    'shortcuts_update_success' => 'تنظیمات میانبر به روز شده است!',
 ];
\ No newline at end of file
index ac235fa024833322fdeacd097f6f96f6a4ad1238..d3018f32b14e9b86f031cdd8363ae0102c814095 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'ترکیب رنگی برنامه',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index 6abb576430388ec93b6d9a3b342eb65e2dd4005e..885ff45b9ebceabd986b3d535cbb0e6c487ac5e9 100644 (file)
@@ -68,9 +68,9 @@ return [
     'user_delete_notification' => 'Utilisateur supprimé avec succès',
 
     // Roles
-    'role_create_notification' => 'Role successfully created',
-    'role_update_notification' => 'Role successfully updated',
-    'role_delete_notification' => 'Role successfully deleted',
+    'role_create_notification' => 'Rôle créé avec succès',
+    'role_update_notification' => 'Rôle mis à jour avec succès',
+    'role_delete_notification' => 'Rôle supprimé avec succès',
 
     // Other
     'commented_on'                => 'a commenté',
index 80536626f70a214ce995466b8878612377467026..7ff76acf5dfc652cc3f9842445dfdab01a9a28b8 100644 (file)
@@ -23,7 +23,7 @@ return [
     'meta_updated' => 'Mis à jour :timeLength',
     'meta_updated_name' => 'Mis à jour :timeLength par :user',
     'meta_owned_name' => 'Appartient à :user',
-    'meta_reference_page_count' => 'Referenced on :count page|Referenced on :count pages',
+    'meta_reference_page_count' => 'Référencé sur :count page|Référencé sur :count pages',
     'entity_select' => 'Sélectionner l\'entité',
     'entity_select_lack_permission' => 'Vous n\'avez pas les permissions requises pour sélectionner cet élément',
     'images' => 'Images',
@@ -63,7 +63,7 @@ return [
     'search_terms' => 'Mot-clé',
     'search_content_type' => 'Type de contenu',
     'search_exact_matches' => 'Correspondances exactes',
-    'search_tags' => 'Recherche par tags',
+    'search_tags' => 'Recherche par mots-clés',
     'search_options' => 'Options',
     'search_viewed_by_me' => 'Vu par moi',
     'search_not_viewed_by_me' => 'Non vu par moi',
@@ -293,21 +293,21 @@ return [
     'tag' => 'Mot-clé',
     'tags' =>  'Mots-clés',
     'tags_index_desc' => 'Les tags peuvent être appliqués au contenu du système pour appliquer une forme flexible de catégorisation. Les tags peuvent avoir à la fois une clé et une valeur, la valeur étant facultative. Une fois appliqué, le contenu peut ensuite être interrogé à l’aide du nom et de la valeur du tag.',
-    'tag_name' =>  'Nom du tag',
+    'tag_name' =>  'Nom du mot-clé',
     'tag_value' => 'Valeur du mot-clé (optionnel)',
     'tags_explain' => "Ajouter des mots-clés pour catégoriser votre contenu.",
     'tags_add' => 'Ajouter un autre mot-clé',
     'tags_remove' => 'Supprimer le mot-clé',
-    'tags_usages' => 'Total des utilisations des tags',
+    'tags_usages' => 'Total des utilisations des mots-clés',
     'tags_assigned_pages' => 'Attribuer aux pages',
     'tags_assigned_chapters' => 'Attribuer aux chapitres',
     'tags_assigned_books' => 'Attribuer aux livres',
     'tags_assigned_shelves' => 'Attribuer aux étagères',
     'tags_x_unique_values' => ':count valeurs uniques',
     'tags_all_values' => 'Toutes les valeurs',
-    'tags_view_tags' => 'Voir les tags',
-    'tags_view_existing_tags' => 'Voir les tags existants',
-    'tags_list_empty_hint' => 'Les tags peuvent être assignés via la barre latérale de l\'éditeur de page ou lors de l\'édition des détails d\'un livre, d\'un chapitre ou d\'une étagère.',
+    'tags_view_tags' => 'Voir les mots-clés',
+    'tags_view_existing_tags' => 'Voir les mots-clés existants',
+    'tags_list_empty_hint' => 'Les mots-clés peuvent être assignés via la barre latérale de l\'éditeur de page ou lors de l\'édition des détails d\'un livre, d\'un chapitre ou d\'une étagère.',
     'attachments' => 'Fichiers joints',
     'attachments_explain' => 'Ajouter des fichiers ou des liens pour les afficher sur votre page. Ils seront affichés dans la barre latérale',
     'attachments_explain_instant_save' => 'Ces changements sont enregistrés immédiatement.',
index d058e829df5c1b0bc627acc03f92ec8af66db78f..8b13d421151761e39a3067c5699bd335c10047bf 100644 (file)
@@ -33,9 +33,9 @@ return [
     'app_custom_html_desc' => 'Le contenu inséré ici sera ajouté en bas de la balise <head> de toutes les pages. Vous pouvez l\'utiliser pour ajouter du CSS personnalisé ou un tracker analytique.',
     'app_custom_html_disabled_notice' => 'Le contenu de l\'en-tête HTML personnalisé est désactivé sur cette page de paramètres pour garantir que les modifications les plus récentes puissent être annulées.',
     'app_logo' => 'Logo de l\'application',
-    'app_logo_desc' => 'This is used in the application header bar, among other areas. This image should be 86px in height. Large images will be scaled down.',
-    'app_icon' => 'Application Icon',
-    'app_icon_desc' => 'This icon is used for browser tabs and shortcut icons. This should be a 256px square PNG image.',
+    'app_logo_desc' => 'Celui-ci est utilisé dans la barre d\'en-tête de l\'application, entre autres zones. L\'image doit être de 86 px de hauteur. Les plus grandes images seront réduites.',
+    'app_icon' => 'Icône de l\'application',
+    'app_icon_desc' => 'Cette icône est utilisée pour les onglets du navigateur et les icônes de raccourci. Doit être une image PNG carrée de 256 px.',
     'app_homepage' => 'Page d\'accueil de l\'application',
     'app_homepage_desc' => 'Choisissez une page à afficher sur la page d\'accueil au lieu de la vue par défaut. Les permissions sont ignorées pour les pages sélectionnées.',
     'app_homepage_select' => 'Choisissez une page',
@@ -49,12 +49,12 @@ return [
     'app_disable_comments_desc' => 'Désactive les commentaires sur toutes les pages de l\'application. Les commentaires existants ne sont pas affichés.',
 
     // Color settings
-    'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
-    'app_color' => 'Primary Color',
-    'link_color' => 'Default Link Color',
-    'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
+    'color_scheme' => 'Schéma de couleurs de l\'application',
+    'color_scheme_desc' => 'Défini les couleurs à utiliser dans l\'interface utilisateur de l\'application. Les couleurs peuvent être configurées séparément pour les modes sombre et clair pour mieux correspondre au thème et assurer la lisibilité.',
+    'ui_colors_desc' => 'Défini la couleur primaire de l\'application et la couleur de lien par défaut. La couleur primaire est principalement utilisée pour la bannière d\'en-tête, les boutons et les décorations de l\'interface. La couleur par défaut du lien est utilisée pour les liens et les actions basées sur le texte, à la fois dans le contenu écrit et dans l\'interface de l\'application.',
+    'app_color' => 'Couleur primaire',
+    'link_color' => 'Couleur de lien par défaut',
+    'content_colors_desc' => 'Défini les couleurs pour tous les éléments de la hiérarchie d\'organisation des pages. Choisir les couleurs avec une luminosité similaire aux couleurs par défaut est recommandé pour la lisibilité.',
     'bookshelf_color' => 'Couleur des étagères',
     'book_color' => 'Couleur des livres',
     'chapter_color' => 'Couleur des chapitres',
@@ -138,7 +138,7 @@ return [
     'roles' => 'Rôles',
     'role_user_roles' => 'Rôles des utilisateurs',
     'roles_index_desc' => 'Les rôles sont utilisés pour regrouper les utilisateurs et fournir une autorisation système à leurs membres. Lorsqu\'un utilisateur est membre de plusieurs rôles, les privilèges accordés se cumulent et l\'utilisateur hérite de tous les droits d\'accès.',
-    'roles_x_users_assigned' => ':count user assigned|:count users assigned',
+    'roles_x_users_assigned' => ':count utilisateur assigné|:count utilisateurs assignés',
     'roles_x_permissions_provided' => ':count permission|:count permissions',
     'roles_assigned_users' => 'Utilisateurs assignés',
     'roles_permissions_provided' => 'Permissions accordées',
@@ -249,7 +249,7 @@ return [
     // Webhooks
     'webhooks' => 'Webhooks',
     'webhooks_index_desc' => 'Les Webhooks sont un moyen d\'envoyer des données à des URL externes lorsque certaines actions et événements se produisent dans le système, ce qui permet une intégration basée sur des événements avec des plates-formes externes telles que les systèmes de messagerie ou de notification.',
-    'webhooks_x_trigger_events' => ':count trigger event|:count trigger events',
+    'webhooks_x_trigger_events' => ':count événement déclencheur|:count événements déclencheurs',
     'webhooks_create' => 'Créer un nouveau Webhook',
     'webhooks_none_created' => 'Aucun webhook n\'a encore été créé.',
     'webhooks_edit' => 'Éditer le Webhook',
index e1e4d423e33ec252ba8626573673ed2090f1f278..c86822ed2bc528caed59afe662f72783b268b18c 100644 (file)
@@ -41,7 +41,7 @@ return [
     'bookshelf_create'            => 'created shelf',
     'bookshelf_create_notification'    => 'Shelf successfully created',
     'bookshelf_create_from_book'    => 'converted book to shelf',
-    'bookshelf_create_from_book_notification'    => 'Book successfully converted to a shelf',
+    'bookshelf_create_from_book_notification'    => 'הספר הוסב בהצלחה למדף',
     'bookshelf_update'                 => 'updated shelf',
     'bookshelf_update_notification'    => 'Shelf successfully updated',
     'bookshelf_delete'                 => 'deleted shelf',
index d14aa7d669db97ac783edfa4c7e0065d9dd55520..b38e349803efa3cac709a54207f3128c6084205d 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index 54e0251bef4a8fbafcab8dc2b5d35df1bbbcc202..a68a630d319f5c2d2aea9b39ca08790ed0eced00 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index 8533becbcd24af960439718e3c19a82dda77b3f3..0d41bd755b58e44fb07c587e9df3738d09d2a135 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Alkalmazás színséma',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Elsődleges szín',
     'link_color' => 'Alapértelmezett link szín',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index 5b28a184efa30139a3081d23d9672fb79239cf60..f131eed4b5f08612e8b39a53677a11897c990acf 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index 2c280395750db5004502bab375176bc81e05e3af..962a14c2f71db1c82d7c8f2d6e4058687ebce49f 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Schema di colore dell\'applicazione',
-    'color_scheme_desc' => 'Imposta i colori da utilizzare nell\'interfaccia di BookStack. I colori possono essere configurati separatamente per le modalità scura e chiara per adattarsi al meglio al tema e garantire la leggibilità.',
-    'ui_colors_desc' => 'Imposta il colore primario e il colore predefinito dei collegamenti per BookStack. Il colore primario è utilizzato principalmente per il banner dell\'intestazione, i pulsanti e le decorazioni dell\'interfaccia. Il colore predefinito dei link è utilizzato per i link e le azioni basate sul testo, sia all\'interno dei contenuti scritti che nell\'interfaccia di Bookstack.',
+    'color_scheme_desc' => 'Impostare i colori da utilizzare nell\'interfaccia utente dell\'applicazione. I colori possono essere configurati separatamente per le modalità scura e chiara, per adattarsi al meglio al tema e garantire la leggibilità.',
+    'ui_colors_desc' => 'Imposta il colore primario dell\'applicazione e il colore predefinito dei collegamenti. Il colore primario è utilizzato principalmente per il banner dell\'intestazione, i pulsanti e le decorazioni dell\'interfaccia. Il colore predefinito dei collegamenti viene utilizzato per i collegamenti e le azioni basate sul testo, sia all\'interno dei contenuti scritti che nell\'interfaccia dell\'applicazione.',
     'app_color' => 'Colore Principale',
     'link_color' => 'Colore preferito del link',
     'content_colors_desc' => 'Impostare i colori per tutti gli elementi nella gerarchia dell\'organizzazione della pagina. Si consiglia di scegliere colori con una luminosità simile a quella dei colori predefiniti per garantire la leggibilità.',
index 7afdf546d93614da1be0800e13d76cbf8c45695a..eeaa55a70914e1d0b57007fcb77cfb114373d35c 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'アプリケーションの配色',
-    'color_scheme_desc' => 'BookStack インターフェイスで使用する色を設定します。 色はダークモードとライトモードで個別に設定することができ、テーマへの適合と読みやすさを確保することができます。',
-    'ui_colors_desc' => 'BookStackのプライマリカラーとデフォルトリンクカラーを設定します。プライマリカラーは主にヘッダーバナー、ボタン、インターフェイスの装飾に使用されます。 デフォルトのリンク色はテキストベースのリンクとアクションに使用されます。これは作成されたコンテンツと Bookstack インターフェイスの両方に適用されます。',
+    'color_scheme_desc' => 'アプリケーションのユーザーインターフェイスで使用する色を設定します。 色はダークモードとライトモードで個別に設定することができ、テーマへの適合と読みやすさを確保することができます。',
+    'ui_colors_desc' => 'アプリケーションのプライマリカラーとデフォルトリンクカラーを設定します。プライマリカラーは主にヘッダーバナー、ボタン、インターフェイスの装飾に使用されます。 デフォルトのリンク色はテキストベースのリンクとアクションに使用されます。これは作成されたコンテンツとアプリケーションインターフェイスの両方に適用されます。',
     'app_color' => 'プライマリ色',
     'link_color' => 'デフォルトのリンク色',
     'content_colors_desc' => 'ページ構成階層の各要素に色を設定します。読みやすさを考慮して、デフォルトの色と同じような明るさの色を選ぶことをお勧めします。',
index 76e689b6d583b6ab2c02ab2c41ca4ff034b591e0..38d817915b82ed3129d80d4f213a3444d8130af7 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index 88ed5245f18e23f93f791d68c412760b03f47612..fc6386aa9164577b46a108c41454e32561c1b1c9 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index e47607473eeb1f2b730b71b3d1ff4e714f6a7445..fddea6150ca756f998affb9069c75df4a6edd284 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index 6f0936e3fc848b47e6eef6e49c3ac506e31e3ce4..1b81baf449920e2a176f6652ed232be010d09022 100644 (file)
@@ -28,8 +28,8 @@ return [
     // Books
     'book_create'                 => 'izveidoja grāmatu',
     'book_create_notification'    => 'Grāmata veiksmīgi izveidota',
-    'book_create_from_chapter'              => 'converted chapter to book',
-    'book_create_from_chapter_notification' => 'Chapter successfully converted to a book',
+    'book_create_from_chapter'              => 'pārveidojā nodaļu par grāmatu',
+    'book_create_from_chapter_notification' => 'Nodaļa veiksmīgi pārveidota par grāmatu',
     'book_update'                 => 'atjaunoja grāmatu',
     'book_update_notification'    => 'Grāmata veiksmīgi atjaunināta',
     'book_delete'                 => 'izdzēsa grāmatu',
@@ -38,14 +38,14 @@ return [
     'book_sort_notification'      => 'Grāmata veiksmīgi pārkārtota',
 
     // Bookshelves
-    'bookshelf_create'            => 'created shelf',
-    'bookshelf_create_notification'    => 'Shelf successfully created',
-    'bookshelf_create_from_book'    => 'converted book to shelf',
-    'bookshelf_create_from_book_notification'    => 'Book successfully converted to a shelf',
-    'bookshelf_update'                 => 'updated shelf',
-    'bookshelf_update_notification'    => 'Shelf successfully updated',
-    'bookshelf_delete'                 => 'deleted shelf',
-    'bookshelf_delete_notification'    => 'Shelf successfully deleted',
+    'bookshelf_create'            => 'izveidoja plauktu',
+    'bookshelf_create_notification'    => 'Plaukts veiksmīgi izveidots',
+    'bookshelf_create_from_book'    => 'pārveidoja grāmatu par plauktu',
+    'bookshelf_create_from_book_notification'    => 'Grāmata veiksmīgi pārveidota par plauktu',
+    'bookshelf_update'                 => 'atjaunoja plauktu',
+    'bookshelf_update_notification'    => 'Plaukts veiksmīgi atjaunināts',
+    'bookshelf_delete'                 => 'izdzēsa plauktu',
+    'bookshelf_delete_notification'    => 'Plaukts veiksmīgi dzēsts',
 
     // Favourites
     'favourite_add_notification' => '":name" ir pievienots jūsu favorītiem',
@@ -68,9 +68,9 @@ return [
     'user_delete_notification' => 'Lietotājs veiksmīgi dzēsts',
 
     // Roles
-    'role_create_notification' => 'Role successfully created',
-    'role_update_notification' => 'Role successfully updated',
-    'role_delete_notification' => 'Role successfully deleted',
+    'role_create_notification' => 'Loma veiksmīgi izveidota',
+    'role_update_notification' => 'Loma veiksmīgi atjaunināta',
+    'role_delete_notification' => 'Loma veiksmīgi dzēsta',
 
     // Other
     'commented_on'                => 'komentēts',
index 1ace7f720f67a82263111f2d73677eae945350a4..d13d93012eb49d63164bde2256d0db3d38819a7b 100644 (file)
@@ -39,9 +39,9 @@ return [
     'register_success' => 'Paldies par reģistrēšanos! Tagad varat pieslēgties.',
 
     // Login auto-initiation
-    'auto_init_starting' => 'Attempting Login',
-    'auto_init_starting_desc' => 'We\'re contacting your authentication system to start the login process. If there\'s no progress after 5 seconds you can try clicking the link below.',
-    'auto_init_start_link' => 'Proceed with authentication',
+    'auto_init_starting' => 'Mēģina pierakstīties',
+    'auto_init_starting_desc' => 'Sazināmies ar jūsu autentifikācijas sistēmu, lai uzsāktu pierakstīšanās procesu. Ja 5 sekunžu laikā nekas nav noticis, mēģiniet klikšķināt zemāk esošo saiti.',
+    'auto_init_start_link' => 'Turpināt autentifikāciju',
 
     // Password Reset
     'reset_password' => 'Atiestatīt paroli',
@@ -61,8 +61,8 @@ return [
     'email_confirm_send_error' => 'E-pasta apriprināšana ir nepieciešama, bet sistēma nevarēja e-pastu nosūtīt. Lūdzu sazinaties ar administratoru, lai pārliecinātos, ka e-pasts ir iestatīts pareizi.',
     'email_confirm_success' => 'Jūsu epasta adrese ir apstiprināta! Jums tagad jābūt iespējai pieslēgties, izmantojot šo epasta adresi.',
     'email_confirm_resent' => 'Apstiprinājuma vēstule tika nosūtīta. Lūdzu, pārbaudiet jūsu e-pastu.',
-    'email_confirm_thanks' => 'Thanks for confirming!',
-    'email_confirm_thanks_desc' => 'Please wait a moment while your confirmation is handled. If you are not redirected after 3 seconds press the "Continue" link below to proceed.',
+    'email_confirm_thanks' => 'Paldies par apstiprinājumu!',
+    'email_confirm_thanks_desc' => 'Lūdzu uzgaidiet, kamēr jūsu apstiprinājums tiek apstrādāts. Ja netiekat novirzīts 3 sekunžu laikā, spiediet saiti "Turpināt", lai dotos tālāk.',
 
     'email_not_confirmed' => 'E-pasts nav apstiprināts',
     'email_not_confirmed_text' => 'Jūsu e-pasta adrese vēl nav apstiprināta.',
index ce8c8ead76a7f5704b47a3aae3b3cb00a9be3012..a63410366d6b1bc84ea25397f7df7da8f476bc76 100644 (file)
@@ -25,7 +25,7 @@ return [
     'actions' => 'Darbības',
     'view' => 'Skatīt',
     'view_all' => 'Skatīt visus',
-    'new' => 'New',
+    'new' => 'Jauns',
     'create' => 'Izveidot',
     'update' => 'Atjaunināt',
     'edit' => 'Rediģēt',
@@ -48,8 +48,8 @@ return [
     'previous' => 'Iepriekšējais',
     'filter_active' => 'Aktīvais filtrs:',
     'filter_clear' => 'Notīrīt filtru',
-    'download' => 'Download',
-    'open_in_tab' => 'Open in Tab',
+    'download' => 'Lejupielādēt',
+    'open_in_tab' => 'Atvērt cilnē',
 
     // Sort Options
     'sort_options' => 'Kārtošanas Opcijas',
@@ -81,14 +81,14 @@ return [
     'none' => 'Neviens',
 
     // Header
-    'homepage' => 'Homepage',
+    'homepage' => 'Sākumlapa',
     'header_menu_expand' => 'Izvērst galvenes izvēlni',
     'profile_menu' => 'Profila izvēlne',
     'view_profile' => 'Apskatīt profilu',
     'edit_profile' => 'Rediģēt profilu',
     'dark_mode' => 'Tumšais režīms',
     'light_mode' => 'Gaišais režīms',
-    'global_search' => 'Global Search',
+    'global_search' => 'Vispārējā meklēšana',
 
     // Layout tabs
     'tab_info' => 'Informācija',
index baba6cb6e1cf0ebc545bfa9778ee82a84df7df0b..9a9026b228dee18c6c1ac4a0b158f50990a3e5c9 100644 (file)
@@ -66,7 +66,7 @@ return [
     'insert_link_title' => 'Ievietot/rediģēt saiti',
     'insert_horizontal_line' => 'Ievietot horizontālu līniju',
     'insert_code_block' => 'Ievietot koda bloku',
-    'edit_code_block' => 'Edit code block',
+    'edit_code_block' => 'Rediģēt koda bloku',
     'insert_drawing' => 'Ievietot/rediģēt zīmējumu',
     'drawing_manager' => 'Zīmēšanas pārvaldnieks',
     'insert_media' => 'Ievietot/rediģēt mediju',
@@ -144,11 +144,11 @@ return [
     'url' => 'URL',
     'text_to_display' => 'Attēlojamais teksts',
     'title' => 'Nosaukums',
-    'open_link' => 'Open link',
-    'open_link_in' => 'Open link in...',
+    'open_link' => 'Atvērt saiti',
+    'open_link_in' => 'Atvērt saiti...',
     'open_link_current' => 'Šis logs',
     'open_link_new' => 'Jauns logs',
-    'remove_link' => 'Remove link',
+    'remove_link' => 'Noņemt saiti',
     'insert_collapsible' => 'Ievietot sakļaujamu bloku',
     'collapsible_unwrap' => 'Attīt',
     'edit_label' => 'Rediģēt marķējumu',
@@ -160,7 +160,7 @@ return [
     'about' => 'Par redaktoru',
     'about_title' => 'Par WYSIWYG redaktoru',
     'editor_license' => 'Redaktora licence un autortiesības',
-    'editor_tiny_license' => 'This editor is built using :tinyLink which is provided under the MIT license.',
+    'editor_tiny_license' => 'Šis redaktors ir izveidots, izmantojot :tinyLink, kas ir publicēts ar MIT licenci.',
     'editor_tiny_license_link' => 'TinyMCE autortiesības un licences detaļas var atrast šeit.',
     'save_continue' => 'Saglabāt lapu un turpināt',
     'callouts_cycle' => '(Turpiniet spiest, lai pārslēgtu tipus)',
index 3a8eea83922787c80ec68ddd86ed77dcfa887dd1..e39199d101d9a78729528c269eb70562d20b6d89 100644 (file)
@@ -23,9 +23,9 @@ return [
     'meta_updated' => 'Atjaunināts :timeLength',
     'meta_updated_name' => ':user atjauninājis pirms :timeLength',
     'meta_owned_name' => 'Īpašnieks :user',
-    'meta_reference_page_count' => 'Referenced on :count page|Referenced on :count pages',
+    'meta_reference_page_count' => 'Atsauce :count lapā|Atsauce :count lapās',
     'entity_select' => 'Izvēlēties vienumu',
-    'entity_select_lack_permission' => 'You don\'t have the required permissions to select this item',
+    'entity_select_lack_permission' => 'Jums nav nepieciešamās piekļuves tiesības, lai izvēlētu šo vienumu',
     'images' => 'Attēli',
     'my_recent_drafts' => 'Mani melnraksti',
     'my_recently_viewed' => 'Mani nesen skatītie',
@@ -44,13 +44,13 @@ return [
     'permissions' => 'Atļaujas',
     'permissions_desc' => 'Set permissions here to override the default permissions provided by user roles.',
     'permissions_book_cascade' => 'Permissions set on books will automatically cascade to child chapters and pages, unless they have their own permissions defined.',
-    'permissions_chapter_cascade' => 'Permissions set on chapters will automatically cascade to child pages, unless they have their own permissions defined.',
+    'permissions_chapter_cascade' => 'Piekļuves tiesības, kas uzstādītas nodaļām, automātiski tiks piešķirtas pakārtotajām lapām, ja vien tām nav atsevišķi norādītas savas piekļuves tiesības.',
     'permissions_save' => 'Saglabāt atļaujas',
     'permissions_owner' => 'Īpašnieks',
-    'permissions_role_everyone_else' => 'Everyone Else',
+    'permissions_role_everyone_else' => 'Visi pārējie',
     'permissions_role_everyone_else_desc' => 'Set permissions for all roles not specifically overridden.',
     'permissions_role_override' => 'Override permissions for role',
-    'permissions_inherit_defaults' => 'Inherit defaults',
+    'permissions_inherit_defaults' => 'Mantot noklusētās vērtības',
 
     // Search
     'search_results' => 'Meklēšanas rezultāti',
@@ -93,23 +93,23 @@ return [
     'shelves_save' => 'Saglabāt plauktu',
     'shelves_books' => 'Grāmatas šajā plauktā',
     'shelves_add_books' => 'Pievienot grāmatas šim plauktam',
-    'shelves_drag_books' => 'Drag books below to add them to this shelf',
+    'shelves_drag_books' => 'Ievelciet grāmatas zemāk, lai novietotu tās šajā plauktā',
     'shelves_empty_contents' => 'Šim gŗamatplauktam nav pievienotu grāmatu',
     'shelves_edit_and_assign' => 'Labot plauktu, lai tam pievienotu grāmatas',
-    'shelves_edit_named' => 'Edit Shelf :name',
-    'shelves_edit' => 'Edit Shelf',
-    'shelves_delete' => 'Delete Shelf',
-    'shelves_delete_named' => 'Delete Shelf :name',
-    'shelves_delete_explain' => "This will delete the shelf with the name ':name'. Contained books will not be deleted.",
-    'shelves_delete_confirmation' => 'Are you sure you want to delete this shelf?',
-    'shelves_permissions' => 'Shelf Permissions',
-    'shelves_permissions_updated' => 'Shelf Permissions Updated',
-    'shelves_permissions_active' => 'Shelf Permissions Active',
-    'shelves_permissions_cascade_warning' => 'Permissions on shelves do not automatically cascade to contained books. This is because a book can exist on multiple shelves. Permissions can however be copied down to child books using the option found below.',
+    'shelves_edit_named' => 'Rediģēt plauktu :name',
+    'shelves_edit' => 'Rediģēt plauktu',
+    'shelves_delete' => 'Dzēst plauktu',
+    'shelves_delete_named' => 'Dzēst plauktu :name',
+    'shelves_delete_explain' => "Tiks dzēsts plaukts ar nosaukumu \":name\". Tajā ievietotās grāmatas netiks dzēstas.",
+    'shelves_delete_confirmation' => 'Vai esat pārliecināts, ka vēlaties dzēst šo plauktu?',
+    'shelves_permissions' => 'Plaukta atļaujas',
+    'shelves_permissions_updated' => 'Plaukta atļaujas atjauninātas',
+    'shelves_permissions_active' => 'Plaukta atļaujas ir aktīvas',
+    'shelves_permissions_cascade_warning' => 'Plauktu piekļuves tiesības netiek automātiski piešķirtas tajā esošajām grāmatām. Tas ir tāpēc, ka grāmata var vienlaicīgi atrasties vairākos plauktos. Tomēr piekļuves tiesības var nokopēt uz plauktam pievienotajām grāmatām, izmantojot zemāk atrodamo opciju.',
     'shelves_copy_permissions_to_books' => 'Kopēt grāmatplaukta atļaujas uz grāmatām',
     'shelves_copy_permissions' => 'Kopēt atļaujas',
-    'shelves_copy_permissions_explain' => 'This will apply the current permission settings of this shelf to all books contained within. Before activating, ensure any changes to the permissions of this shelf have been saved.',
-    'shelves_copy_permission_success' => 'Shelf permissions copied to :count books',
+    'shelves_copy_permissions_explain' => 'Pašreizējās plaukta piekļuves tiesības tiks piemērotas visām tajā esošajām grāmatām. Pirms ieslēgšanas pārliecinieties, ka visas izmaiņas plaukta piekļuves tiesības ir saglabātas.',
+    'shelves_copy_permission_success' => 'Plaukta piekļuves tiesības kopētas uz :count grāmatām',
 
     // Books
     'book' => 'Grāmata',
@@ -141,7 +141,7 @@ return [
     'books_search_this' => 'Meklēt šajā grāmatā',
     'books_navigation' => 'Grāmatas navigācija',
     'books_sort' => 'Kārtot grāmatas saturu',
-    'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.',
+    'books_sort_desc' => 'Pārvietojiet nodaļas un lapas grāmatas ietvaros, lai organizētu tās saturu. Var pievienot vēl citas grāmatas, lai vieglāk pārcelt nodaļas un lapas starp grāmatām.',
     'books_sort_named' => 'Kārtot grāmatu :bookName',
     'books_sort_name' => 'Kārtot pēc nosaukuma',
     'books_sort_created' => 'Kārtot pēc izveidošanas datuma',
@@ -150,17 +150,17 @@ return [
     'books_sort_chapters_last' => 'Nodaļas pēdējās',
     'books_sort_show_other' => 'Rādīt citas grāmatas',
     'books_sort_save' => 'Saglabāt jauno kārtību',
-    'books_sort_show_other_desc' => 'Add other books here to include them in the sort operation, and allow easy cross-book reorganisation.',
-    'books_sort_move_up' => 'Move Up',
-    'books_sort_move_down' => 'Move Down',
-    'books_sort_move_prev_book' => 'Move to Previous Book',
-    'books_sort_move_next_book' => 'Move to Next Book',
-    'books_sort_move_prev_chapter' => 'Move Into Previous Chapter',
-    'books_sort_move_next_chapter' => 'Move Into Next Chapter',
-    'books_sort_move_book_start' => 'Move to Start of Book',
-    'books_sort_move_book_end' => 'Move to End of Book',
-    'books_sort_move_before_chapter' => 'Move to Before Chapter',
-    'books_sort_move_after_chapter' => 'Move to After Chapter',
+    'books_sort_show_other_desc' => 'Pievienojiet citas grāmatas šeit, lai tās iekļautu kārtošanā un pieļautu vienkāršāku satura organizēšanu starp grāmatām.',
+    'books_sort_move_up' => 'Pārvietot uz augšu',
+    'books_sort_move_down' => 'Pārvietot uz leju',
+    'books_sort_move_prev_book' => 'Pārvietot uz iepriekšējo grāmatu',
+    'books_sort_move_next_book' => 'Pārvietot uz nākamo grāmatu',
+    'books_sort_move_prev_chapter' => 'Pārvietot uz iepriekšējo nodaļu',
+    'books_sort_move_next_chapter' => 'Pārvietot uz nākamo nodaļu',
+    'books_sort_move_book_start' => 'Pārvietot uz grāmatas sākumu',
+    'books_sort_move_book_end' => 'Pārvietot uz grāmatas beigām',
+    'books_sort_move_before_chapter' => 'Pārvietot pirms nodaļas',
+    'books_sort_move_after_chapter' => 'Pārvietot pēc nodaļas',
     'books_copy' => 'Kopēt grāmatu',
     'books_copy_success' => 'Grāmata veiksmīgi nokopēta',
 
@@ -188,7 +188,7 @@ return [
     'chapters_permissions_active' => 'Nodaļas atļaujas ir aktīvas',
     'chapters_permissions_success' => 'Nodaļas atļaujas ir atjauninātas',
     'chapters_search_this' => 'Meklēt šajā nodaļā',
-    'chapter_sort_book' => 'Sort Book',
+    'chapter_sort_book' => 'Kārtot grāmatu',
 
     // Pages
     'page' => 'Lapa',
@@ -236,7 +236,7 @@ return [
     'pages_md_insert_image' => 'Ievietot attēlu',
     'pages_md_insert_link' => 'Ievietot vienuma saiti',
     'pages_md_insert_drawing' => 'Ievietot zīmējumu',
-    'pages_md_show_preview' => 'Show preview',
+    'pages_md_show_preview' => 'Rādīt priekšskatu',
     'pages_md_sync_scroll' => 'Sync preview scroll',
     'pages_not_in_chapter' => 'Lapa nav nodaļā',
     'pages_move' => 'Pārvietot lapu',
@@ -248,14 +248,14 @@ return [
     'pages_permissions_success' => 'Lapas atļaujas atjauninātas',
     'pages_revision' => 'Revīzijas',
     'pages_revisions' => 'Lapas revīzijas',
-    'pages_revisions_desc' => 'Listed below are all the past revisions of this page. You can look back upon, compare, and restore old page versions if permissions allow. The full history of the page may not be fully reflected here since, depending on system configuration, old revisions could be auto-deleted.',
+    'pages_revisions_desc' => 'Zemāk norādītas visas šīs lapas pagātnes versijas. Jūs varat pārskatīt, salīdzināt un atjaunot vecākas versijas, ja to atļauj jūsu piekļuves tiesības. Pilna lapas vēsture varētu netikt attēlota, jo, atkarībā no sistēmas uzstādījumiem, vecākas versijas varētu būt dzēstas automātiski.',
     'pages_revisions_named' => ':pageName lapas revīzijas',
     'pages_revision_named' => ':pageName lapas revīzija',
     'pages_revision_restored_from' => 'Atjaunots no #:id; :summary',
     'pages_revisions_created_by' => 'Izveidoja',
     'pages_revisions_date' => 'Revīzijas datums',
     'pages_revisions_number' => '#',
-    'pages_revisions_sort_number' => 'Revision Number',
+    'pages_revisions_sort_number' => 'Versijas numurs',
     'pages_revisions_numbered' => 'Revīzija #:id',
     'pages_revisions_numbered_changes' => 'Revīzijas #:id izmaiņas',
     'pages_revisions_editor' => 'Redaktora veids',
@@ -269,7 +269,7 @@ return [
     'pages_edit_content_link' => 'Labot saturu',
     'pages_permissions_active' => 'Lapas atļaujas ir aktīvas',
     'pages_initial_revision' => 'Sākotnējā publikācija',
-    'pages_references_update_revision' => 'System auto-update of internal links',
+    'pages_references_update_revision' => 'Automātiska iekšējo saišu atjaunināšana',
     'pages_initial_name' => 'Jauna lapa',
     'pages_editing_draft_notification' => 'Jūs pašlaik veicat izmaiņas melnrakstā, kurš pēdējo reizi ir saglabāts :timeDiff.',
     'pages_draft_edited_notification' => 'Šī lapa ir tikusi atjaunināta. Šo melnrakstu ieteicams atmest.',
@@ -382,19 +382,19 @@ return [
     'copy_consider_access' => 'Atrašanās vietas, īpašnieka vai piekļuves tiesību izmaiņas var padarīt šo saturu pieejamu citiem, kam iepriekš nav dota piekļuve.',
 
     // Conversions
-    'convert_to_shelf' => 'Convert to Shelf',
-    'convert_to_shelf_contents_desc' => 'You can convert this book to a new shelf with the same contents. Chapters contained within this book will be converted to new books. If this book contains any pages, that are not in a chapter, this book will be renamed and contain such pages, and this book will become part of the new shelf.',
+    'convert_to_shelf' => 'Pārveidot par plauktu',
+    'convert_to_shelf_contents_desc' => 'Jūs varat pārveidot šo grāmatu par jaunu plauktu ar to pašu saturu. Nodaļas šajā grāmatā tiks pārveidots par jaunām grāmatām. Ja šī grāmata satur atsevišķas lapas, kas neietilpst nevienā nodaļā, tiks izveidota atsevišķa grāmata ar šādām lapām, kas tiks ievietota jaunajā plauktā.',
     'convert_to_shelf_permissions_desc' => 'Any permissions set on this book will be copied to the new shelf and to all new child books that don\'t have their own permissions enforced. Note that permissions on shelves do not auto-cascade to content within, as they do for books.',
-    'convert_book' => 'Convert Book',
-    'convert_book_confirm' => 'Are you sure you want to convert this book?',
-    'convert_undo_warning' => 'This cannot be as easily undone.',
-    'convert_to_book' => 'Convert to Book',
-    'convert_to_book_desc' => 'You can convert this chapter to a new book with the same contents. Any permissions set on this chapter will be copied to the new book but any inherited permissions, from the parent book, will not be copied which could lead to a change of access control.',
-    'convert_chapter' => 'Convert Chapter',
-    'convert_chapter_confirm' => 'Are you sure you want to convert this chapter?',
+    'convert_book' => 'Pārveidot grāmatu',
+    'convert_book_confirm' => 'Vai tiešām vēlaties pārveidot šo grāmatu?',
+    'convert_undo_warning' => 'To nav iespiejāms vienkārši atcelt.',
+    'convert_to_book' => 'Pārveidot par grāmatu',
+    'convert_to_book_desc' => 'Jūs varat pārveidot šo nodaļu par grāmatu ar tādu pašu saturu. Visas piekļuves tiesības, kas uzstādītas šai nodaļai, tiks kopētas uz jauno grāmatu, taču piekļuves tiesības, kas tiek uzstādītas pašreizējai grāmatai kopumā, netiks kopētas, tā kā ir iespējams, ka jaunajai grāmati var būt citas piekļuves tiesības.',
+    'convert_chapter' => 'Pārveidot nodaļu',
+    'convert_chapter_confirm' => 'Vai tiešām vēlaties pārveidot šo nodaļu?',
 
     // References
-    'references' => 'References',
-    'references_none' => 'There are no tracked references to this item.',
-    'references_to_desc' => 'Shown below are all the known pages in the system that link to this item.',
+    'references' => 'Atsauces',
+    'references_none' => 'Uz šo vienumu nav atrasta neviena atsauce.',
+    'references_to_desc' => 'Zemāk parādītas visas sistēmā atrastās lapas, kas norāda uz šo vienumu.',
 ];
index 4e9b5b29a0434e49e5924e2a33e58161c9719eda..3c697716da43d9570f7f410cc42e706f0b24fcd2 100644 (file)
@@ -50,7 +50,7 @@ return [
     // Drawing & Images
     'image_upload_error' => 'Radās kļūda augšupielādējot attēlu',
     'image_upload_type_error' => 'Ielādējamā attēla tips nav derīgs',
-    'drawing_data_not_found' => 'Drawing data could not be loaded. The drawing file might no longer exist or you may not have permission to access it.',
+    'drawing_data_not_found' => 'Attēla datus nevarēja ielādēt. Attēla fails, iespējams, vairs neeksistē, vai arī jums varētu nebūt piekļuves tiesības tam.',
 
     // Attachments
     'attachment_not_found' => 'Pielikums nav atrasts',
@@ -61,7 +61,7 @@ return [
 
     // Entities
     'entity_not_found' => 'Vienība nav atrasta',
-    'bookshelf_not_found' => 'Shelf not found',
+    'bookshelf_not_found' => 'Plaukts nav atrasts',
     'book_not_found' => 'Grāmata nav atrasta',
     'page_not_found' => 'Lapa nav atrasta',
     'chapter_not_found' => 'Nodaļa nav atrasta',
index 947f23735dfafcab07b4fd510f06d562da4dcbeb..3d89c88c76ac92149730b0ecef9132dbe6487d3d 100644 (file)
@@ -33,9 +33,9 @@ return [
     'app_custom_html_desc' => 'Šis saturs tiks pievienots <head> sadaļas apakšā visām lapām. Tas ir noderīgi papildinot CSS stilus vai pievienojot analītikas kodu.',
     'app_custom_html_disabled_notice' => 'Pielāgots HTML head saturs ir izslēgts šajā uzstādījumu lapā, lai nodrošinātu, ka iespējams atcelt jebkādas kritiskas izmaiņas.',
     'app_logo' => 'Lietotnes logo',
-    'app_logo_desc' => 'This is used in the application header bar, among other areas. This image should be 86px in height. Large images will be scaled down.',
-    'app_icon' => 'Application Icon',
-    'app_icon_desc' => 'This icon is used for browser tabs and shortcut icons. This should be a 256px square PNG image.',
+    'app_logo_desc' => 'Tas tiek izmantots lietotnes galvenē un citās vietās. Attēlam jābūt 86px augstam, lieli attēli tiks samazināti.',
+    'app_icon' => 'Lietotnes ikona',
+    'app_icon_desc' => 'Ikona tiek izmantota pārlūka cilnēm un īsceļiem. Tai jābūt 256px kvadrātveida PNG attēlam.',
     'app_homepage' => 'Aplikācijas sākumlapa',
     'app_homepage_desc' => 'Izvēlēties skatu, ko rādīt sākumlapā noklusētā skata vietā. Lapas piekļuves tiesības izvēlētajai lapai netiks ņemtas vērā.',
     'app_homepage_select' => 'Izvēlēties lapu',
@@ -49,12 +49,12 @@ return [
     'app_disable_comments_desc' => 'Atslēdz komentārus visās aplikācijas lapās.<br> Jau eksistējoši komentāri netiks attēloti.',
 
     // Color settings
-    'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
-    'app_color' => 'Primary Color',
-    'link_color' => 'Default Link Color',
-    'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
+    'color_scheme' => 'Lietotnes krāsu shēma',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
+    'app_color' => 'Pamatkrāsa',
+    'link_color' => 'Noklusētā saišu krāsa',
+    'content_colors_desc' => 'Norādīt krāsas visiem lapas hierarhijas elementiem. Lasāmības labad ieteicams izvēlēties krāsas ar līdzīgu spilgtumu kā noklusētajām.',
     'bookshelf_color' => 'Plaukta krāsa',
     'book_color' => 'Grāmatas krāsa',
     'chapter_color' => 'Nodaļas krāsa',
index 2adfb02ae78649a4964a1174da64fb50e24d2820..f1bdc12a25022f922648d44254e73dd96df0101a 100644 (file)
@@ -237,7 +237,7 @@ return [
     'pages_md_insert_link' => 'Sett inn lenke',
     'pages_md_insert_drawing' => 'Sett inn tegning',
     'pages_md_show_preview' => 'Forhåndsvisning',
-    'pages_md_sync_scroll' => 'Sync preview scroll',
+    'pages_md_sync_scroll' => 'Synkroniser forhåndsvisningsrulle',
     'pages_not_in_chapter' => 'Siden tilhører ingen kapittel',
     'pages_move' => 'Flytt side',
     'pages_move_success' => 'Siden ble flyttet til «:parentName»',
@@ -248,7 +248,7 @@ return [
     'pages_permissions_success' => 'Sidens tilganger ble endret',
     'pages_revision' => 'Revisjon',
     'pages_revisions' => 'Sidens revisjoner',
-    'pages_revisions_desc' => 'Listed below are all the past revisions of this page. You can look back upon, compare, and restore old page versions if permissions allow. The full history of the page may not be fully reflected here since, depending on system configuration, old revisions could be auto-deleted.',
+    'pages_revisions_desc' => 'Oppført nedenfor er alle tidligere revisjoner av denne siden. Du kan se tilbake igjen, sammenligne og gjenopprette tidligere sideversjoner hvis du tillater det. Den hele sidens historikk kan kanskje ikke gjenspeiles fullstendig her, avhengig av systemkonfigurasjonen, kan gamle revisjoner bli slettet automatisk.',
     'pages_revisions_named' => 'Revisjoner for :pageName',
     'pages_revision_named' => 'Revisjoner for :pageName',
     'pages_revision_restored_from' => 'Gjenopprettet fra #:id; :summary',
@@ -292,17 +292,17 @@ return [
     'shelf_tags' => 'Hyllemerker',
     'tag' => 'Merke',
     'tags' =>  'Merker',
-    'tags_index_desc' => 'Tags can be applied to content within the system to apply a flexible form of categorization. Tags can have both a key and value, with the value being optional. Once applied, content can then be queried using the tag name and value.',
+    'tags_index_desc' => 'Merker kan brukes på innhold i systemet for å anvende en kategorisering på en fleksibel måte. Etiketter kan ha både en nøkkel og verdi, med valgfri. Når det er brukt, kan innhold sjekkes ved hjelp av taggnavn og verdi.',
     'tag_name' =>  'Merketittel',
     'tag_value' => 'Merkeverdi (Valgfritt)',
     'tags_explain' => "Legg til merker for å kategorisere innholdet ditt. \n Du kan legge til merkeverdier for å beskrive dem ytterligere.",
     'tags_add' => 'Legg til flere merker',
     'tags_remove' => 'Fjern merke',
-    'tags_usages' => 'Total tag usages',
-    'tags_assigned_pages' => 'Assigned to Pages',
-    'tags_assigned_chapters' => 'Assigned to Chapters',
-    'tags_assigned_books' => 'Assigned to Books',
-    'tags_assigned_shelves' => 'Assigned to Shelves',
+    'tags_usages' => 'Totalt emneordbruk',
+    'tags_assigned_pages' => 'Tilordnet sider',
+    'tags_assigned_chapters' => 'Tildelt til kapitler',
+    'tags_assigned_books' => 'Tilordnet til bøker',
+    'tags_assigned_shelves' => 'Tilordnet hyller',
     'tags_x_unique_values' => ':count unike verdier',
     'tags_all_values' => 'Alle verdier',
     'tags_view_tags' => 'Vis etiketter',
@@ -374,27 +374,27 @@ return [
     'revision_cannot_delete_latest' => 'CKan ikke slette siste revisjon.',
 
     // Copy view
-    'copy_consider' => 'Please consider the below when copying content.',
-    'copy_consider_permissions' => 'Custom permission settings will not be copied.',
-    'copy_consider_owner' => 'You will become the owner of all copied content.',
-    'copy_consider_images' => 'Page image files will not be duplicated & the original images will retain their relation to the page they were originally uploaded to.',
-    'copy_consider_attachments' => 'Page attachments will not be copied.',
-    'copy_consider_access' => 'A change of location, owner or permissions may result in this content being accessible to those previously without access.',
+    'copy_consider' => 'Vennligst vurder nedenfor når du kopierer innholdet.',
+    'copy_consider_permissions' => 'Egendefinerte tilgangsinnstillinger vil ikke bli kopiert.',
+    'copy_consider_owner' => 'Du vil bli eier av alt kopiert innhold.',
+    'copy_consider_images' => 'Sidebildefiler vil ikke bli duplisert og de opprinnelige bildene beholder relasjonen til siden de opprinnelig ble lastet opp til.',
+    'copy_consider_attachments' => 'Sidevedlegg vil ikke bli kopiert.',
+    'copy_consider_access' => 'Endring av sted, eier eller rettigheter kan føre til at innholdet er tilgjengelig for dem som tidligere har vært uten adgang.',
 
     // Conversions
-    'convert_to_shelf' => 'Convert to Shelf',
-    'convert_to_shelf_contents_desc' => 'You can convert this book to a new shelf with the same contents. Chapters contained within this book will be converted to new books. If this book contains any pages, that are not in a chapter, this book will be renamed and contain such pages, and this book will become part of the new shelf.',
-    'convert_to_shelf_permissions_desc' => 'Any permissions set on this book will be copied to the new shelf and to all new child books that don\'t have their own permissions enforced. Note that permissions on shelves do not auto-cascade to content within, as they do for books.',
-    'convert_book' => 'Convert Book',
-    'convert_book_confirm' => 'Are you sure you want to convert this book?',
-    'convert_undo_warning' => 'This cannot be as easily undone.',
-    'convert_to_book' => 'Convert to Book',
-    'convert_to_book_desc' => 'You can convert this chapter to a new book with the same contents. Any permissions set on this chapter will be copied to the new book but any inherited permissions, from the parent book, will not be copied which could lead to a change of access control.',
-    'convert_chapter' => 'Convert Chapter',
-    'convert_chapter_confirm' => 'Are you sure you want to convert this chapter?',
+    'convert_to_shelf' => 'Konverter til bokhylle',
+    'convert_to_shelf_contents_desc' => 'Du kan konvertere denne boken til en ny hylle med samme innhold. Kapitteler i denne boken vil bli konvertert til nye bøker. Hvis boken inneholder noen sider, som ikke er i et kapitler, boka blir omdøpt og med slike sider, og boka blir en del av den nye bokhyllen.',
+    'convert_to_shelf_permissions_desc' => 'Eventuelle tillatelser som er satt på denne boka, vil bli kopiert til ny hylle og til alle nye under-bøker som ikke har egne tillatelser satt. Vær oppmerksom på at tillatelser på hyllene ikke skjuler automatisk innhold innenfor, da de gjør for bøker.',
+    'convert_book' => 'Konverter bok',
+    'convert_book_confirm' => 'Er du sikker på at du vil konvertere denne boken?',
+    'convert_undo_warning' => 'Dette kan ikke bli så lett å angre.',
+    'convert_to_book' => 'Konverter til bok',
+    'convert_to_book_desc' => 'Du kan konvertere kapittelet til en ny bok med samme innhold. Alle tillatelser som er angitt i dette kapittelet vil bli kopiert til den nye boken, men alle arvede tillatelser, fra overordnet bok vil ikke kopieres noe som kan føre til en endring av tilgangskontroll.',
+    'convert_chapter' => 'Konverter kapittel',
+    'convert_chapter_confirm' => 'Er du sikker på at du vil konvertere dette kapittelet?',
 
     // References
-    'references' => 'References',
-    'references_none' => 'There are no tracked references to this item.',
-    'references_to_desc' => 'Shown below are all the known pages in the system that link to this item.',
+    'references' => 'Referanser',
+    'references_none' => 'Det er ingen sporede referanser til dette elementet.',
+    'references_to_desc' => 'Nedenfor vises alle de kjente sidene i systemet som lenker til denne oppføringen.',
 ];
index 66eb2bb78da5793a8527789082c69f5ddcc24b13..fe420c1d47a31ea4f3f77754e24e470182a2e33b 100644 (file)
@@ -23,10 +23,10 @@ return [
     'saml_no_email_address' => 'Denne kontoinformasjonen finnes ikke i det eksterne autentiseringssystemet.',
     'saml_invalid_response_id' => 'Forespørselen fra det eksterne autentiseringssystemet gjenkjennes ikke av en prosess som startes av dette programmet. Å navigere tilbake etter pålogging kan forårsake dette problemet.',
     'saml_fail_authed' => 'Innlogging gjennom :system feilet. Fikk ikke kontakt med autentiseringstjeneren.',
-    'oidc_already_logged_in' => 'Already logged in',
-    'oidc_user_not_registered' => 'The user :name is not registered and automatic registration is disabled',
-    'oidc_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system',
-    'oidc_fail_authed' => 'Login using :system failed, system did not provide successful authorization',
+    'oidc_already_logged_in' => 'Allerede logget inn',
+    'oidc_user_not_registered' => 'Brukeren :name er ikke registrert og automatisk registrering er deaktivert',
+    'oidc_no_email_address' => 'Finner ikke en e-postadresse, for denne brukeren, i dataene som leveres av det eksterne autentiseringssystemet',
+    'oidc_fail_authed' => 'Innlogging ved hjelp av :system feilet, systemet ga ikke vellykket godkjenning',
     'social_no_action_defined' => 'Ingen handlinger er definert',
     'social_login_bad_response' => "Feilmelding mottat fra :socialAccount innloggingstjeneste: \n:error",
     'social_account_in_use' => 'Denne :socialAccount kontoen er allerede registrert, Prøv å logge inn med :socialAccount alternativet.',
@@ -50,7 +50,7 @@ return [
     // Drawing & Images
     'image_upload_error' => 'Bildet kunne ikke lastes opp, forsøk igjen.',
     'image_upload_type_error' => 'Bildeformatet støttes ikke, forsøk med et annet format.',
-    'drawing_data_not_found' => 'Drawing data could not be loaded. The drawing file might no longer exist or you may not have permission to access it.',
+    'drawing_data_not_found' => 'Tegningsdata kunne ikke lastes. Det er mulig at tegningsfilen ikke finnes lenger, eller du har ikke rettigheter til å få tilgang til den.',
 
     // Attachments
     'attachment_not_found' => 'Vedlegget ble ikke funnet',
@@ -61,7 +61,7 @@ return [
 
     // Entities
     'entity_not_found' => 'Entitet ble ikke funnet',
-    'bookshelf_not_found' => 'Shelf not found',
+    'bookshelf_not_found' => 'Bokhyllen ble ikke funnet',
     'book_not_found' => 'Boken ble ikke funnet',
     'page_not_found' => 'Siden ble ikke funnet',
     'chapter_not_found' => 'Kapittel ble ikke funnet',
index e9a47461b3d18a42a68699d729acf99b07b9eda5..bc066d03564cebfd6db0904511993b5016ae02d1 100644 (file)
@@ -5,14 +5,14 @@
  */
 
 return [
-    'shortcuts' => 'Shortcuts',
-    'shortcuts_interface' => 'Interface Keyboard Shortcuts',
-    'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.',
-    'shortcuts_customize_desc' => 'You can customize each of the shortcuts below. Just press your desired key combination after selecting the input for a shortcut.',
-    'shortcuts_toggle_label' => 'Keyboard shortcuts enabled',
-    'shortcuts_section_navigation' => 'Navigation',
-    'shortcuts_section_actions' => 'Common Actions',
-    'shortcuts_save' => 'Save Shortcuts',
-    'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.',
-    'shortcuts_update_success' => 'Shortcut preferences have been updated!',
+    'shortcuts' => 'Snarveier',
+    'shortcuts_interface' => 'Grensesnitt hurtigtaster',
+    'shortcuts_toggle_desc' => 'Her kan du aktivere eller deaktivere snarveier for tastatur system som brukes til navigasjon og handlinger.',
+    'shortcuts_customize_desc' => 'Du kan tilpasse hver av snarveiene nedenfor. Trykk på ønsket nøkkelkombinasjon etter å ha valgt inndata for en snarvei.',
+    'shortcuts_toggle_label' => 'Tastatursnarveier aktivert',
+    'shortcuts_section_navigation' => 'Navigasjon',
+    'shortcuts_section_actions' => 'Vanlige handlinger',
+    'shortcuts_save' => 'Lagre snarveier',
+    'shortcuts_overlay_desc' => 'Merk: Når snarveier er aktivert er et hjelperoverlegg tilgjengelig via å trykke "?" som vil fremheve de tilgjengelige snarveiene som for øyeblikket er synlige på skjermen.',
+    'shortcuts_update_success' => 'Snarvei innstillinger er oppdatert!',
 ];
\ No newline at end of file
index 3b76a40c734e5fc31cd340dd4f8993601c64a6d4..9130786268dab12acd0bc709f891443c3d25708d 100644 (file)
@@ -10,8 +10,8 @@ return [
     'settings' => 'Innstillinger',
     'settings_save' => 'Lagre innstillinger',
     'settings_save_success' => 'Innstillinger lagret',
-    'system_version' => 'System Version',
-    'categories' => 'Categories',
+    'system_version' => 'System versjon',
+    'categories' => 'Kategorier',
 
     // App Settings
     'app_customization' => 'Tilpassing',
@@ -27,15 +27,15 @@ return [
     'app_secure_images' => 'Høyere sikkerhet på bildeopplastinger',
     'app_secure_images_toggle' => 'Enable høyere sikkerhet på bildeopplastinger',
     'app_secure_images_desc' => 'Av ytelsesgrunner er alle bilder offentlige. Dette alternativet legger til en tilfeldig streng som er vanskelig å gjette foran bildets nettadresser. Forsikre deg om at katalogindekser ikke er aktivert for å forhindre enkel tilgang.',
-    'app_default_editor' => 'Default Page Editor',
-    'app_default_editor_desc' => 'Select which editor will be used by default when editing new pages. This can be overridden at a page level where permissions allow.',
+    'app_default_editor' => 'Standard sideredigeringsprogram',
+    'app_default_editor_desc' => 'Velg hvilken tekstbehandler som skal brukes som standard når du redigerer nye sider. Dette kan overskrives på et sidenivå der tillatelser tillates.',
     'app_custom_html' => 'Tilpasset HTML-hodeinnhold',
     'app_custom_html_desc' => 'Alt innhold som legges til her, blir satt inn i bunnen av <head> -delen på hver side. Dette er praktisk for å overstyre stiler eller legge til analysekode.',
     'app_custom_html_disabled_notice' => 'Tilpasset HTML-hodeinnhold er deaktivert på denne innstillingssiden for å sikre at eventuelle endringer ødelegger noe, kan tilbakestilles.',
     'app_logo' => 'Applikasjonslogo',
-    'app_logo_desc' => 'This is used in the application header bar, among other areas. This image should be 86px in height. Large images will be scaled down.',
-    'app_icon' => 'Application Icon',
-    'app_icon_desc' => 'This icon is used for browser tabs and shortcut icons. This should be a 256px square PNG image.',
+    'app_logo_desc' => 'Dette brukes i programtoppfeltet blant andre områder. Dette bildet skal være 86px i høyde. Store bilder vil bli skalert ned.',
+    'app_icon' => 'Applikasjons ikon',
+    'app_icon_desc' => 'Dette ikonet brukes for nettleserfaner og snarveisikoner. Dette bør være et bilde på 256 px kvadrat PNG.',
     'app_homepage' => 'Applikasjonens hjemmeside',
     'app_homepage_desc' => 'Velg en visning som skal vises på hjemmesiden i stedet for standardvisningen. Sidetillatelser ignoreres for utvalgte sider.',
     'app_homepage_select' => 'Velg en side',
@@ -49,12 +49,12 @@ return [
     'app_disable_comments_desc' => 'Deaktiver kommentarer på tvers av alle sidene i applikasjonen. <br> Eksisterende kommentarer vises ikke.',
 
     // Color settings
-    'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
-    'app_color' => 'Primary Color',
-    'link_color' => 'Default Link Color',
-    'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
+    'color_scheme' => 'Applikasjonens farge oppsett',
+    'color_scheme_desc' => 'Sett farger for å bruke i programmets brukergrensesnitt. Farger kan konfigureres separat for mørke og lysmoduser for å passe best inn temaet og sørge for lesbarhet.',
+    'ui_colors_desc' => 'Angi primær farge for programmet og standard link farge. Primær farge brukes hovedsakelig for toppbanner, knapper og grensesnittets dekorasjoner. Standardfargen for koblinger brukes for tekstbaserte lenker og handlinger, både i skriftlig innhold og i programgrensesnittet.',
+    'app_color' => 'Primær farge',
+    'link_color' => 'Standard koblingsfarge',
+    'content_colors_desc' => 'Angi farger for alle elementer i organiseringshierarkiet. Velger du farger med lik lysstyrke til standard farger anbefales for lesbarhet.',
     'bookshelf_color' => 'Hyllefarge',
     'book_color' => 'Bokfarge',
     'chapter_color' => 'Kapittelfarge',
@@ -93,10 +93,10 @@ return [
     'maint_send_test_email_mail_text' => 'Gratulerer! Da du mottok dette e-postvarselet, ser det ut til at e-postinnstillingene dine er konfigurert riktig.',
     'maint_recycle_bin_desc' => 'Slettede hyller, bøker, kapitler og sider kastes i papirkurven så de kan bli gjenopprettet eller slettet permanent. Eldre utgaver i papirkurven kan slettes automatisk etter en stund, avhengig av systemkonfigurasjonen.',
     'maint_recycle_bin_open' => 'Åpne papirkurven',
-    'maint_regen_references' => 'Regenerate References',
-    'maint_regen_references_desc' => 'This action will rebuild the cross-item reference index within the database. This is usually handled automatically but this action can be useful to index old content or content added via unofficial methods.',
-    'maint_regen_references_success' => 'Reference index has been regenerated!',
-    'maint_timeout_command_note' => 'Note: This action can take time to run, which can lead to timeout issues in some web environments. As an alternative, this action be performed using a terminal command.',
+    'maint_regen_references' => 'Regenerer referanser',
+    'maint_regen_references_desc' => 'Denne handlingen gjenoppbygger referanseindeksen for krysselement i databasen. Dette håndteres vanligvis automatisk, men denne handlingen kan være nyttig for å indeksere gammelt innhold eller innhold lagt til via uoffisielle metoder.',
+    'maint_regen_references_success' => 'Referanseindeksen har blitt regenerert!',
+    'maint_timeout_command_note' => 'Merk: Denne handlingen kan ta tid å kjøre, noe som kan føre til tidsavbruddsmessige problemer i noen webomgivelser. Dette gjøres som et alternativ ved hjelp av en terminalkommando.',
 
     // Recycle Bin
     'recycle_bin' => 'Papirkurven',
@@ -129,7 +129,7 @@ return [
     'audit_table_user' => 'Kontoholder',
     'audit_table_event' => 'Hendelse',
     'audit_table_related' => 'Relaterte elementer eller detaljer',
-    'audit_table_ip' => 'IP Address',
+    'audit_table_ip' => 'IP Adresse',
     'audit_table_date' => 'Aktivitetsdato',
     'audit_date_from' => 'Datoperiode fra',
     'audit_date_to' => 'Datoperiode til',
@@ -137,13 +137,13 @@ return [
     // Role Settings
     'roles' => 'Roller',
     'role_user_roles' => 'Kontoroller',
-    'roles_index_desc' => 'Roles are used to group users & provide system permission to their members. When a user is a member of multiple roles the privileges granted will stack and the user will inherit all abilities.',
-    'roles_x_users_assigned' => ':count user assigned|:count users assigned',
-    'roles_x_permissions_provided' => ':count permission|:count permissions',
-    'roles_assigned_users' => 'Assigned Users',
-    'roles_permissions_provided' => 'Provided Permissions',
+    'roles_index_desc' => 'Roller brukes til å gruppere brukere og gi systemtilgang til medlemmene. Når en bruker er medlem av flere roller, vil de tildelte rettighetene samles inn, og brukeren vil arve alle evner.',
+    'roles_x_users_assigned' => ':count bruker tildelt|:count brukere tildelt',
+    'roles_x_permissions_provided' => ':count tillatelse|:count tillatelser',
+    'roles_assigned_users' => 'Tilordnede brukere',
+    'roles_permissions_provided' => 'Tilbudte rettigheter',
     'role_create' => 'Opprett ny rolle',
-    'role_delete' => 'Rolle slettet',
+    'role_delete' => 'Slett rolle',
     'role_delete_confirm' => 'Dette vil slette rollen «:roleName».',
     'role_delete_users_assigned' => 'Denne rollen har :userCount kontoer koblet opp mot seg. Velg hvilke rolle du vil flytte disse til.',
     'role_delete_no_migration' => "Ikke flytt kontoer",
@@ -162,13 +162,13 @@ return [
     'role_manage_page_templates' => 'Behandle sidemaler',
     'role_access_api' => 'Systemtilgang API',
     'role_manage_settings' => 'Behandle applikasjonsinnstillinger',
-    'role_export_content' => 'Export content',
-    'role_editor_change' => 'Change page editor',
+    'role_export_content' => 'Eksporter innhold',
+    'role_editor_change' => 'Endre sideredigering',
     'role_asset' => 'Eiendomstillatelser',
     'roles_system_warning' => 'Vær oppmerksom på at tilgang til noen av de ovennevnte tre tillatelsene kan tillate en bruker å endre sine egne rettigheter eller rettighetene til andre i systemet. Bare tildel roller med disse tillatelsene til pålitelige brukere.',
     'role_asset_desc' => 'Disse tillatelsene kontrollerer standard tilgang til eiendelene i systemet. Tillatelser til bøker, kapitler og sider overstyrer disse tillatelsene.',
     'role_asset_admins' => 'Administratorer får automatisk tilgang til alt innhold, men disse alternativene kan vise eller skjule UI-alternativer.',
-    'role_asset_image_view_note' => 'This relates to visibility within the image manager. Actual access of uploaded image files will be dependant upon system image storage option.',
+    'role_asset_image_view_note' => 'Dette gjelder synlighet innenfor bilde-administrasjonen. Faktisk tilgang på opplastede bildefiler vil være avhengig av valget for systemlagring av bildet.',
     'role_all' => 'Alle',
     'role_own' => 'Egne',
     'role_controlled_by_asset' => 'Kontrollert av eiendelen de er lastet opp til',
@@ -178,7 +178,7 @@ return [
 
     // Users
     'users' => 'Brukere',
-    'users_index_desc' => 'Create & manage individual user accounts within the system. User accounts are used for login and attribution of content & activity. Access permissions are primarily role-based but user content ownership, among other factors, may also affect permissions & access.',
+    'users_index_desc' => 'Opprett og administrer individuelle brukerkontoer innenfor systemet. Brukerkontoer brukes for innlogging og navngivelse av innhold og aktivitet. Tilgangstillatelser er primært rollebasert, men brukerinnhold eierskap, blant andre faktorer, kan også påvirke tillatelser og tilgang.',
     'user_profile' => 'Profil',
     'users_add_new' => 'Register ny konto',
     'users_search' => 'Søk i kontoer',
@@ -189,7 +189,7 @@ return [
     'users_role' => 'Roller',
     'users_role_desc' => 'Velg hvilke roller denne kontoholderen vil bli tildelt. Hvis en kontoholderen er tildelt flere roller, vil tillatelsene fra disse rollene stable seg, og de vil motta alle evnene til de tildelte rollene.',
     'users_password' => 'Passord',
-    'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 8 characters long.',
+    'users_password_desc' => 'Angi et passord som brukes til å logge inn til programmet. Dette må være minst 8 tegn langt.',
     'users_send_invite_text' => 'Du kan velge å sende denne kontoholderen en invitasjons-e-post som lar dem angi sitt eget passord, ellers kan du selv angi passordet.',
     'users_send_invite_option' => 'Send invitasjonsmelding',
     'users_external_auth_id' => 'Ekstern godkjennings-ID',
@@ -248,32 +248,32 @@ return [
 
     // Webhooks
     'webhooks' => 'Webhooks',
-    'webhooks_index_desc' => 'Webhooks are a way to send data to external URLs when certain actions and events occur within the system which allows event-based integration with external platforms such as messaging or notification systems.',
-    'webhooks_x_trigger_events' => ':count trigger event|:count trigger events',
-    'webhooks_create' => 'Create New Webhook',
-    'webhooks_none_created' => 'No webhooks have yet been created.',
-    'webhooks_edit' => 'Edit Webhook',
-    'webhooks_save' => 'Save Webhook',
-    'webhooks_details' => 'Webhook Details',
-    'webhooks_details_desc' => 'Provide a user friendly name and a POST endpoint as a location for the webhook data to be sent to.',
-    'webhooks_events' => 'Webhook Events',
-    'webhooks_events_desc' => 'Select all the events that should trigger this webhook to be called.',
-    'webhooks_events_warning' => 'Keep in mind that these events will be triggered for all selected events, even if custom permissions are applied. Ensure that use of this webhook won\'t expose confidential content.',
-    'webhooks_events_all' => 'All system events',
-    'webhooks_name' => 'Webhook Name',
-    'webhooks_timeout' => 'Webhook Request Timeout (Seconds)',
-    'webhooks_endpoint' => 'Webhook Endpoint',
-    'webhooks_active' => 'Webhook Active',
-    'webhook_events_table_header' => 'Events',
-    'webhooks_delete' => 'Delete Webhook',
-    'webhooks_delete_warning' => 'This will fully delete this webhook, with the name \':webhookName\', from the system.',
-    'webhooks_delete_confirm' => 'Are you sure you want to delete this webhook?',
-    'webhooks_format_example' => 'Webhook Format Example',
-    'webhooks_format_example_desc' => 'Webhook data is sent as a POST request to the configured endpoint as JSON following the format below. The "related_item" and "url" properties are optional and will depend on the type of event triggered.',
-    'webhooks_status' => 'Webhook Status',
-    'webhooks_last_called' => 'Last Called:',
-    'webhooks_last_errored' => 'Last Errored:',
-    'webhooks_last_error_message' => 'Last Error Message:',
+    'webhooks_index_desc' => 'Webhooks er en måte å sende data til eksterne nettadresser når bestemte handlinger og hendelser oppstår i systemet som gjør det mulig å integrer med eksterne plattformer som meldingssystemer eller varslingssystemer.',
+    'webhooks_x_trigger_events' => ':count utløsende hendelse:count utløsende hendelser',
+    'webhooks_create' => 'Lag ny Webhook',
+    'webhooks_none_created' => 'Ingen webhooks er opprettet ennå.',
+    'webhooks_edit' => 'Rediger webhook',
+    'webhooks_save' => 'Lagre Webhook',
+    'webhooks_details' => 'Webhook detaljer',
+    'webhooks_details_desc' => 'Gi et brukervennlig navn og et POST endepunkt som et sted der webhook-dataene skal sendes til.',
+    'webhooks_events' => 'Webhook hendelser',
+    'webhooks_events_desc' => 'Velg alle hendelsene som skal utløse denne webhook som skal kalles.',
+    'webhooks_events_warning' => 'Husk at disse hendelsene vil bli utløst for alle valgte hendelser, selv om egendefinerte tillatelser brukes. Pass på at bruk av denne webhooken ikke vil utsette konfidensiell innhold.',
+    'webhooks_events_all' => 'Alle systemhendelser',
+    'webhooks_name' => 'Webhook navn',
+    'webhooks_timeout' => 'Tidsavbrudd for Webhook forespørsler (sekunder)',
+    'webhooks_endpoint' => 'Webhook endepunkt',
+    'webhooks_active' => 'Webhook aktiv',
+    'webhook_events_table_header' => 'Hendelser',
+    'webhooks_delete' => 'Slett webhook',
+    'webhooks_delete_warning' => 'Dette vil slette webhook, med navnet \':webhookName\', fra systemet.',
+    'webhooks_delete_confirm' => 'Er du sikker på at du vil slette denne webhooken?',
+    'webhooks_format_example' => 'Webhook formattering eksempel',
+    'webhooks_format_example_desc' => 'Webhook-data sendes som en POST-forespørsel til det konfigurerte endepunktet som JSON ved hjelp av formatet nedenfor. «related_item» og «url» egenskaper er valgfrie og vil avhenge av hvilken type hendelse som utløses.',
+    'webhooks_status' => 'Webhook status',
+    'webhooks_last_called' => 'Sist ringt:',
+    'webhooks_last_errored' => 'Siste feil:',
+    'webhooks_last_error_message' => 'Siste feilmelding:',
 
 
     //! If editing translations files directly please ignore this in all
index 0e8917f3772dcad691b37549922bc5a97e25e8b3..7e3784e30c70c4ebccc74f41d9ea4a14133fdcaa 100644 (file)
@@ -32,7 +32,7 @@ return [
     'digits_between'       => ':attribute må være mellomg :min og :max tall.',
     'email'                => ':attribute må være en gyldig e-post.',
     'ends_with' => ':attribute må slutte med en av verdiene: :values',
-    'file'                 => 'The :attribute must be provided as a valid file.',
+    'file'                 => 'Attributtet :attribute må angis som en gyldig fil.',
     'filled'               => ':attribute feltet er påkrevd.',
     'gt'                   => [
         'numeric' => ':attribute må være større enn :value.',
index 73c41df9dd3faa010f379816a1e9eff0a4942e6a..310be612b9f0e7d2b5394f3d0413eb7a845db3a4 100644 (file)
@@ -68,9 +68,9 @@ return [
     'user_delete_notification' => 'Gebruiker succesvol verwijderd',
 
     // Roles
-    'role_create_notification' => 'Role successfully created',
-    'role_update_notification' => 'Role successfully updated',
-    'role_delete_notification' => 'Role successfully deleted',
+    'role_create_notification' => 'Rol succesvol aangemaakt',
+    'role_update_notification' => 'Rol succesvol bijgewerkt',
+    'role_delete_notification' => 'Rol succesvol verwijderd',
 
     // Other
     'commented_on'                => 'reageerde op',
index 7774528505720138ba770d8413bba1f3399f1adb..bac2868fce1d6854679cb48b8c7d7f242f13885a 100644 (file)
@@ -23,7 +23,7 @@ return [
     'meta_updated' => 'Bijgewerkt: :timeLength',
     'meta_updated_name' => 'Bijgewerkt: :timeLength door :user',
     'meta_owned_name' => 'Eigendom van :user',
-    'meta_reference_page_count' => 'Referenced on :count page|Referenced on :count pages',
+    'meta_reference_page_count' => 'Naartoe verwezen op :count pagina|Naartoe verwezen op :count pagina\'s',
     'entity_select' => 'Entiteit selecteren',
     'entity_select_lack_permission' => 'Je hebt niet de vereiste machtiging om dit item te selecteren',
     'images' => 'Afbeeldingen',
@@ -141,7 +141,7 @@ return [
     'books_search_this' => 'Zoeken in dit boek',
     'books_navigation' => 'Boek navigatie',
     'books_sort' => 'Inhoud van het boek sorteren',
-    'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.',
+    'books_sort_desc' => 'Verplaats hoofdstukken en pagina\'s binnen een boek om de inhoud ervan te reorganiseren. Andere boeken kunnen worden toegevoegd, zodat hoofdstukken en pagina\'s gemakkelijk tussen boeken kunnen worden verplaatst.',
     'books_sort_named' => 'Sorteer boek :bookName',
     'books_sort_name' => 'Sorteren op naam',
     'books_sort_created' => 'Sorteren op datum van aanmaken',
@@ -150,17 +150,17 @@ return [
     'books_sort_chapters_last' => 'Hoofdstukken laatst',
     'books_sort_show_other' => 'Bekijk andere boeken',
     'books_sort_save' => 'Nieuwe volgorde opslaan',
-    'books_sort_show_other_desc' => 'Add other books here to include them in the sort operation, and allow easy cross-book reorganisation.',
-    'books_sort_move_up' => 'Move Up',
-    'books_sort_move_down' => 'Move Down',
-    'books_sort_move_prev_book' => 'Move to Previous Book',
-    'books_sort_move_next_book' => 'Move to Next Book',
-    'books_sort_move_prev_chapter' => 'Move Into Previous Chapter',
-    'books_sort_move_next_chapter' => 'Move Into Next Chapter',
-    'books_sort_move_book_start' => 'Move to Start of Book',
-    'books_sort_move_book_end' => 'Move to End of Book',
-    'books_sort_move_before_chapter' => 'Move to Before Chapter',
-    'books_sort_move_after_chapter' => 'Move to After Chapter',
+    'books_sort_show_other_desc' => 'Voeg hier andere boeken toe om ze op te nemen in de sortering, en om een gemakkelijke reorganisatie van boeken mogelijk te maken.',
+    'books_sort_move_up' => 'Verplaats naar boven',
+    'books_sort_move_down' => 'Verplaats naar beneden',
+    'books_sort_move_prev_book' => 'Verplaats naar het vorige boek',
+    'books_sort_move_next_book' => 'Verplaats naar het volgende boek',
+    'books_sort_move_prev_chapter' => 'Verplaats naar het vorige hoofdstuk',
+    'books_sort_move_next_chapter' => 'Verplaats naar het volgende hoofdstuk',
+    'books_sort_move_book_start' => 'Verplaats naar het begin van het boek',
+    'books_sort_move_book_end' => 'Verplaats naar het einde van het boek',
+    'books_sort_move_before_chapter' => 'Verplaats naar vóór het hoofdstuk',
+    'books_sort_move_after_chapter' => 'Verplaats naar áchter het hoofdstuk',
     'books_copy' => 'Kopieer Boek',
     'books_copy_success' => 'Boek succesvol gekopieerd',
 
index 920579e978223873b6663dcb70adae53056ac0a8..a78862eae4674ce9dfc526d1de46d77b55c04aba 100644 (file)
@@ -50,7 +50,7 @@ return [
     // Drawing & Images
     'image_upload_error' => 'Er is een fout opgetreden bij het uploaden van de afbeelding',
     'image_upload_type_error' => 'Het geüploade afbeeldingstype is ongeldig',
-    'drawing_data_not_found' => 'Drawing data could not be loaded. The drawing file might no longer exist or you may not have permission to access it.',
+    'drawing_data_not_found' => 'De gegevens van de tekening konden niet worden geladen. Het tekenbestand bestaat misschien niet meer of u hebt geen machtiging om het te openen.',
 
     // Attachments
     'attachment_not_found' => 'Bijlage niet gevonden',
index 644beb4b1147c17c651cb1e258f0341b508fa760..05981f59309e479f1a8758d4a42e6ebcfc4395b8 100644 (file)
@@ -33,9 +33,9 @@ return [
     'app_custom_html_desc' => 'Alle hieronder toegevoegde data wordt aan het einde van de <head> sectie van elke pagina toegevoegd. Gebruik dit om stijlen te overschrijven of analytische code toe te voegen.',
     'app_custom_html_disabled_notice' => 'Bovenstaande wordt niet toegevoegd aan deze pagina om ervoor te zorgen dat je foutieve code steeds ongedaan kan maken.',
     'app_logo' => 'Applicatielogo',
-    'app_logo_desc' => 'This is used in the application header bar, among other areas. This image should be 86px in height. Large images will be scaled down.',
-    'app_icon' => 'Application Icon',
-    'app_icon_desc' => 'This icon is used for browser tabs and shortcut icons. This should be a 256px square PNG image.',
+    'app_logo_desc' => 'Dit wordt onder meer gebruikt in de kopbalk van de applicatie. Deze afbeelding dient 86px hoog te zijn. Grote afbeeldingen worden teruggeschaald.',
+    'app_icon' => 'Applicatie Pictogram',
+    'app_icon_desc' => 'Dit pictogram wordt gebruikt voor browsertabbladen en snelkoppelingspictogrammen. Dit dient een 256px vierkante PNG-afbeelding te zijn.',
     'app_homepage' => 'Applicatie Startpagina',
     'app_homepage_desc' => 'Selecteer een weergave om weer te geven op de startpagina in plaats van de standaard weergave. Paginamachtigingen worden genegeerd voor geselecteerde pagina\'s.',
     'app_homepage_select' => 'Selecteer een pagina',
@@ -49,12 +49,12 @@ return [
     'app_disable_comments_desc' => 'Schakel reacties uit op alle pagina\'s in de applicatie. <br> Bestaande reacties worden niet getoond.',
 
     // Color settings
-    'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
-    'app_color' => 'Primary Color',
-    'link_color' => 'Default Link Color',
-    'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
+    'color_scheme' => 'Kleurenschema van applicatie',
+    'color_scheme_desc' => 'Stel de kleuren in voor de gebruikersinterface van de applicatie. Kleuren kunnen afzonderlijk worden geconfigureerd voor donkere en lichte modi om zo goed mogelijk bij het thema te passen en de leesbaarheid te garanderen.',
+    'ui_colors_desc' => 'Stel de primaire kleur van de applicatie en de standaard hyperlinkkleur in. De primaire kleur wordt voornamelijk gebruikt voor de headerbanner, knoppen en interfacedecoraties. De standaard hyperlinkkleur wordt gebruikt voor tekstgebaseerde links en acties, zowel binnen geschreven inhoud als in de applicatie-interface.',
+    'app_color' => 'Primaire kleur',
+    'link_color' => 'Standaard hyperlinkkleur',
+    'content_colors_desc' => 'Stel kleuren in voor alle elementen in de hiërarchie van de pagina-organisatie. Voor de leesbaarheid is het aan te raden kleuren te kiezen met eenzelfde helderheid als de standaardkleuren.',
     'bookshelf_color' => 'Kleur van de Boekenplank',
     'book_color' => 'Kleur van het Boek',
     'chapter_color' => 'Kleur van het Hoofdstuk',
@@ -138,8 +138,8 @@ return [
     'roles' => 'Rollen',
     'role_user_roles' => 'Gebruikersrollen',
     'roles_index_desc' => 'Rollen worden gebruikt om gebruikers te groeperen en systeemrechten te geven. Wanneer een gebruiker lid is van meerdere rollen worden de toegekende rechten samengevoegd en erft de gebruiker alle mogelijkheden.',
-    'roles_x_users_assigned' => ':count user assigned|:count users assigned',
-    'roles_x_permissions_provided' => ':count permission|:count permissions',
+    'roles_x_users_assigned' => ':count gebruiker toegewezen|:count gebruikers toegewezen',
+    'roles_x_permissions_provided' => ':count machtiging|:count machtigingen',
     'roles_assigned_users' => 'Toegewezen Gebruikers',
     'roles_permissions_provided' => 'Verleende Machtigingen',
     'role_create' => 'Nieuwe Rol Maken',
index b644425fa898680dc54deff6de400c526d5f55c1..bfc8a56e8184d786418ae9669da1daf3bdfecef8 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Schemat kolorów aplikacji',
-    'color_scheme_desc' => 'Ustaw kolory użyte w interfejsie BookStack\'a. Kolory można skonfigurować oddzielnie dla trybu ciemnego i jasnego, aby jak najlepiej pasowały do motywu i zapewniały czytelność.',
-    'ui_colors_desc' => 'Ustaw kolor podstawowy i domyślny kolor linku dla BookStack\'a. Podstawowy kolor jest używany głównie do głównego baneru, przycisków i dekoracji interfejsu. Domyślny kolor linku jest używany dla tekstowych linków i akcji, zarówno w napisanej treści, jak i w interfejsie Bookstack\'a.',
+    'color_scheme_desc' => 'Ustaw kolory używane w interfejsie aplikacji. Kolory można skonfigurować oddzielnie dla trybu ciemnego i jasnego, aby najlepiej pasowały do motywu i zapewniały czytelność.',
+    'ui_colors_desc' => 'Ustaw podstawowy kolor aplikacji i domyślny kolor linku. Podstawowy kolor jest używany głównie w banerze aplikacji, przyciskach i interfejsie. Domyślny kolor linku jest używany dla tekstowych linków i akcji, zarówno w napisanych treściach, jak i w interfejsie aplikacji.',
     'app_color' => 'Kolor podstawowy',
     'link_color' => 'Domyślny kolor linku',
     'content_colors_desc' => 'Ustaw kolory dla wszystkich elementów w hierarchii organizacji stron. Wybór kolorów o jasności podobnej do domyślnych kolorów jest zalecany dla czytelności.',
index a362ae4bd20ecd9f2e0c548a6f882f4a7c6b183c..65d6b74c5f3b3c503173690875c8ad2deafc5892 100644 (file)
@@ -68,9 +68,9 @@ return [
     'user_delete_notification' => 'Utilizador removido com sucesso',
 
     // Roles
-    'role_create_notification' => 'Role successfully created',
-    'role_update_notification' => 'Role successfully updated',
-    'role_delete_notification' => 'Role successfully deleted',
+    'role_create_notification' => 'Cargo criado com sucesso',
+    'role_update_notification' => 'Cargo atualizado com sucesso',
+    'role_delete_notification' => 'Cargo excluído com sucesso',
 
     // Other
     'commented_on'                => 'comentado a',
index 4c8f87faf2571b429beac1dd1e1c4934a4a33820..347196a0dac890a5ab86440dad622a6dad07fc4e 100644 (file)
@@ -23,7 +23,7 @@ return [
     'meta_updated' => 'Atualizado :timeLength',
     'meta_updated_name' => 'Atualizado :timeLength por :user',
     'meta_owned_name' => 'Propriedade de :user',
-    'meta_reference_page_count' => 'Referenced on :count page|Referenced on :count pages',
+    'meta_reference_page_count' => 'Referenciado em :count página|Referenciado em :count páginas',
     'entity_select' => 'Seleção de Entidade',
     'entity_select_lack_permission' => 'Não tem as permissões necessárias para selecionar este item',
     'images' => 'Imagens',
index 3c678b8daeea05547021211e774304a53edecfeb..3cbc83753ce9ebe21c1a7ae8d4ff7a2eefa6fa41 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Esquema de cores da aplicação',
-    'color_scheme_desc' => 'Define as cores a serem usadas na interface do BookStack. As cores podem ser configuradas separadamente para modos escuro e claro para melhor se adequar ao tema e garantir a legibilidade.',
-    'ui_colors_desc' => 'Defina a cor primária e a cor padrão do link para o BookStack. A cor primária é usada principalmente para o banner do cabeçalho, botões e decorações da interface. A cor padrão do link é usada para links e ações baseados em texto, tanto no conteúdo escrito quanto na interface do BookStack.',
+    'color_scheme_desc' => 'Defina as cores a serem utilizadas na aplicação. As cores podem ser configuradas separadamente para modos escuro e claro para melhor se adequar ao tema e garantir legibilidade.',
+    'ui_colors_desc' => 'Defina a cor primária e a cor padrão para links da aplicação. A cor principal é utilizada principalmente para o banner do cabeçalho, botões e decorações da interface. A cor padrão do link é usada para links e ações baseados em texto, tanto dentro do conteúdo escrito quanto na interface da aplicação.',
     'app_color' => 'Cor primária',
     'link_color' => 'Cor padrão do link',
     'content_colors_desc' => 'Definir cores para todos os elementos na hierarquia da organização da página. Escolher cores com um brilho semelhante às cores padrão é recomendado para a legibilidade.',
@@ -138,8 +138,8 @@ return [
     'roles' => 'Cargos',
     'role_user_roles' => 'Cargos de Utilizador',
     'roles_index_desc' => 'Papéis são usados para agrupar utilizadores & fornecer permissão ao sistema para os seus membros. Quando um utilizador é membro de múltiplas funções, os privilégios concedidos irão acumular e o utilizador herdará todas as habilidades.',
-    'roles_x_users_assigned' => ':count user assigned|:count users assigned',
-    'roles_x_permissions_provided' => ':count permission|:count permissions',
+    'roles_x_users_assigned' => ':count utilizadores atribuído|:count utilizadores atribuídos',
+    'roles_x_permissions_provided' => ':count permissão|:count permissões',
     'roles_assigned_users' => 'Utilizadores atribuídos',
     'roles_permissions_provided' => 'Permissões fornecidas',
     'role_create' => 'Criar novo Cargo',
@@ -249,7 +249,7 @@ return [
     // Webhooks
     'webhooks' => 'Webhooks',
     'webhooks_index_desc' => 'Webhooks são uma maneira de enviar dados para URLs externas quando certas ações e eventos ocorrem no sistema. Isto permite uma integração baseada em eventos com plataformas externas como mensagens ou sistemas de notificação.',
-    'webhooks_x_trigger_events' => ':count trigger event|:count trigger events',
+    'webhooks_x_trigger_events' => ':count acionador|:count acionadores',
     'webhooks_create' => 'Criar um novo webhook',
     'webhooks_none_created' => 'Ainda nenhum webhooks foi criado.',
     'webhooks_edit' => 'Editar Webhook',
index 870f5ab73e3c32717da6b47bffd5b14a0ee5b446..6375afb56c492b1102c0b33862ec66374912030e 100644 (file)
@@ -68,9 +68,9 @@ return [
     'user_delete_notification' => 'Usuário removido com sucesso',
 
     // Roles
-    'role_create_notification' => 'Role successfully created',
-    'role_update_notification' => 'Role successfully updated',
-    'role_delete_notification' => 'Role successfully deleted',
+    'role_create_notification' => 'Perfil criado com sucesso',
+    'role_update_notification' => 'Perfil atualizado com sucesso',
+    'role_delete_notification' => 'Perfil excluído com sucesso',
 
     // Other
     'commented_on'                => 'comentou em',
index ac16aaa31cacd3de6f4ff3b8b2aad75bc37a83e2..34843670aec3b8b5b9c8546c445b5f8f0ba9679f 100644 (file)
@@ -50,7 +50,7 @@ return [
     // Drawing & Images
     'image_upload_error' => 'Um erro aconteceu enquanto o servidor tentava efetuar o upload da imagem',
     'image_upload_type_error' => 'O tipo de imagem que está sendo enviada é inválido',
-    'drawing_data_not_found' => 'Drawing data could not be loaded. The drawing file might no longer exist or you may not have permission to access it.',
+    'drawing_data_not_found' => 'Dados de desenho não puderam ser carregados. Talvez o arquivo de desenho não exista mais ou você não tenha permissão para acessá-lo.',
 
     // Attachments
     'attachment_not_found' => 'Anexo não encontrado',
index 3242316e26812bb11b51a7e30fc155c1d5ff993e..33841bab065457907bb10c8de8ad66dbad5f2849 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index d90cae4a95c8397f2577b1104dfa69d75be83dd0..26d8a505f2ae84c6bffd533bc827165b9e4afaf2 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index 43cc8a05c85c291164d4a674e323be5d2d3b14c9..46d72c17d491e548367f83a8d89d5c3a76406d05 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Цветовая схема приложения',
-    'color_scheme_desc' => 'Установите цвета для использования в интерфейсе BookStack. Цвета могут быть настроены отдельно для темных и светлых режимов, чтобы наилучшим образом соответствовать теме и обеспечить разборчивость.',
-    'ui_colors_desc' => 'Установите основной цвет и цвет ссылок по умолчанию для BookStack. Основной цвет в основном используется для заголовка баннера, кнопок и декораций интерфейса. Цвет ссылок по умолчанию используется для текстовых ссылок и действий как в письменном содержании, так и в интерфейсе Bookstack.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Основной цвет',
     'link_color' => 'Цвет ссылки',
     'content_colors_desc' => 'Задает цвета для всех элементов организационной иерархии страницы. Для удобства чтения рекомендуется выбирать цвета, яркость которых близка к цветам по умолчанию.',
index a3852438aadc0a63e0537b4b34da738b30ea810c..9f337815b9b566ea254af16ddbd00d72ac02ca59 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index 042a945dc2ba255eb4deff12aa74cd3daa8369fe..7a77d9f3103654240e5108f073d96f5c46f293a2 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index c5c91692c4706e48ae2626362879699773c76eac..a0273ae79b1b7e7d4b97579fc8c61958ad14bbaa 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index a9b112164381cdbc72d538c7778a19e1c7c5b18c..2d317f522cdb212d9f2ffef4a4bdbca247420b17 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index 18b9dabfde2fff0b10499756567dc958917df02c..a7bf465240e540f5d0e3bb28910b3d8f8b2a6d67 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Колірна схема застосунку',
-    'color_scheme_desc' => 'Встановлює колір для інтерфейсу закладок. Кольори можуть бути налаштовані окремо для темних та світлих режимів, щоб пристосуватися до теми та забезпечити роздільну здатність.',
-    'ui_colors_desc' => 'Установіть основний колір і колір посилання за замовчуванням для BookStack. Основний колір в основному використовується для банера заголовка, кнопок і декорацій інтерфейсу. Стандартний колір посилання використовується для текстових посилань і дій як у письмовому вмісті, так і в інтерфейсі Bookstack.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Головний колір',
     'link_color' => 'Колір посилання за замовчуванням',
     'content_colors_desc' => 'Установіть кольори для всіх елементів в ієрархії організації сторінки. Для зручності читання рекомендується вибирати кольори з такою ж яскравістю, як і кольори за замовчуванням.',
index 76e689b6d583b6ab2c02ab2c41ca4ff034b591e0..38d817915b82ed3129d80d4f213a3444d8130af7 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index 9f53489736658483eef48ef6bec8e3d906664ef8..48125e0fbcee703b5cebae968512b76067ff7d68 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Ứng dụng bảng màu',
-    'color_scheme_desc' => 'Đặt các màu sẽ sử dụng trong giao diện BookStack. Màu sắc có thể được định cấu hình riêng cho chế độ tối và sáng để phù hợp nhất với chủ đề và đảm bảo mức độ rõ ràng.',
-    'ui_colors_desc' => 'Thiết lập màu chính và màu liên kết mặc định cho BookStack. Màu cơ bản được sử dụng chủ yếu cho banner tiêu đề, các nút và trang trí giao diện. Màu liên kết mặc định được sử dụng cho các liên kết và hành động dựa trên văn bản, cả trong nội dung bằng văn bản và trong giao diện Ngăn xếp sách.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Màu cơ bản',
     'link_color' => 'Màu liên kết mặc định',
     'content_colors_desc' => 'Đặt màu cho tất cả các thành phần trong phân cấp tổ chức trang. Nên chọn màu có độ sáng tương tự với màu mặc định để có thể đọc được.',
index 13036bd09462dd59fa2a449ce5f0ccfc2aa0a2fa..06c7458aeb040def561ce05faf8eb8be45b57dca 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => '应用程序配色方案',
-    'color_scheme_desc' => '设置要在 BookStack 界面中使用的颜色。 可以为深色和浅色模式分别配置颜色,以适合主题并确保易读性。',
-    'ui_colors_desc' => '设置 BookStack 的主颜色和默认链接颜色。主颜色主要用于页眉横幅、按钮和界面装饰。默认链接颜色用于基于文本的链接和操作,包括编写界面和 Bookstack 界面。',
+    'color_scheme_desc' => '设置要在应用程序界面中使用的颜色。 可以为深色和浅色模式分别配置颜色,以适合主题并确保易读性。',
+    'ui_colors_desc' => '设置应用程序的主颜色和默认链接颜色。主颜色主要用于页眉横幅、按钮和界面装饰。默认链接颜色用于基于文本的链接和操作,包括编写界面和应用程序界面。',
     'app_color' => '主颜色',
     'link_color' => '默认链接颜色',
     'content_colors_desc' => '为页面组织层次结构中的所有元素设置颜色。为了便于阅读,建议选择与默认颜色亮度相似的颜色。',
index d1673a5972c6eaa22e680dadc87c205c140bfbc5..734e26cf5218936203987e83b743da1fef4aa246 100644 (file)
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
+    'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
index eff4bea7d66230f2898afe53e85e739a6d3fc325..bb8b6049be4cc2a729c73c0f74b735ec67a41bab 100644 (file)
@@ -1,12 +1,27 @@
 {
   "name": "bookstack",
-  "lockfileVersion": 2,
+  "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "dependencies": {
-        "clipboard": "^2.0.11",
-        "codemirror": "^5.65.5",
+        "@codemirror/commands": "^6.2.2",
+        "@codemirror/lang-css": "^6.1.1",
+        "@codemirror/lang-html": "^6.4.3",
+        "@codemirror/lang-javascript": "^6.1.6",
+        "@codemirror/lang-json": "^6.0.1",
+        "@codemirror/lang-markdown": "^6.1.1",
+        "@codemirror/lang-php": "^6.0.1",
+        "@codemirror/lang-xml": "^6.0.2",
+        "@codemirror/language": "^6.6.0",
+        "@codemirror/legacy-modes": "^6.3.2",
+        "@codemirror/state": "^6.2.0",
+        "@codemirror/theme-one-dark": "^6.1.1",
+        "@codemirror/view": "^6.9.4",
+        "@lezer/highlight": "^1.1.4",
+        "@ssddanbrown/codemirror-lang-smarty": "^1.0.0",
+        "@ssddanbrown/codemirror-lang-twig": "^1.0.0",
+        "codemirror": "^6.0.1",
         "dropzone": "^5.9.3",
         "markdown-it": "^13.0.1",
         "markdown-it-task-lists": "^2.1.1",
         "sortablejs": "^1.15.0"
       },
       "devDependencies": {
+        "@lezer/generator": "^1.2.2",
         "chokidar-cli": "^3.0",
-        "esbuild": "^0.17.3",
+        "esbuild": "^0.17.16",
+        "eslint": "^8.38.0",
+        "eslint-config-airbnb-base": "^15.0.0",
+        "eslint-plugin-import": "^2.27.5",
         "livereload": "^0.9.3",
         "npm-run-all": "^4.1.5",
         "punycode": "^2.3.0",
-        "sass": "^1.57.0"
+        "sass": "^1.62.0"
+      }
+    },
+    "node_modules/@codemirror/autocomplete": {
+      "version": "6.5.1",
+      "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.5.1.tgz",
+      "integrity": "sha512-/Sv9yJmqyILbZ26U4LBHnAtbikuVxWUp+rQ8BXuRGtxZfbfKOY/WPbsUtvSP2h0ZUZMlkxV/hqbKRFzowlA6xw==",
+      "dependencies": {
+        "@codemirror/language": "^6.0.0",
+        "@codemirror/state": "^6.0.0",
+        "@codemirror/view": "^6.6.0",
+        "@lezer/common": "^1.0.0"
+      },
+      "peerDependencies": {
+        "@codemirror/language": "^6.0.0",
+        "@codemirror/state": "^6.0.0",
+        "@codemirror/view": "^6.0.0",
+        "@lezer/common": "^1.0.0"
+      }
+    },
+    "node_modules/@codemirror/commands": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.2.2.tgz",
+      "integrity": "sha512-s9lPVW7TxXrI/7voZ+HmD/yiAlwAYn9PH5SUVSUhsxXHhv4yl5eZ3KLntSoTynfdgVYM0oIpccQEWRBQgmNZyw==",
+      "dependencies": {
+        "@codemirror/language": "^6.0.0",
+        "@codemirror/state": "^6.2.0",
+        "@codemirror/view": "^6.0.0",
+        "@lezer/common": "^1.0.0"
+      }
+    },
+    "node_modules/@codemirror/lang-css": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.1.1.tgz",
+      "integrity": "sha512-P6jdNEHyRcqqDgbvHYyC9Wxkek0rnG3a9aVSRi4a7WrjPbQtBTaOmvYpXmm13zZMAatO4Oqpac+0QZs7sy+LnQ==",
+      "dependencies": {
+        "@codemirror/autocomplete": "^6.0.0",
+        "@codemirror/language": "^6.0.0",
+        "@codemirror/state": "^6.0.0",
+        "@lezer/css": "^1.0.0"
+      }
+    },
+    "node_modules/@codemirror/lang-html": {
+      "version": "6.4.3",
+      "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.3.tgz",
+      "integrity": "sha512-VKzQXEC8nL69Jg2hvAFPBwOdZNvL8tMFOrdFwWpU+wc6a6KEkndJ/19R5xSaglNX6v2bttm8uIEFYxdQDcIZVQ==",
+      "dependencies": {
+        "@codemirror/autocomplete": "^6.0.0",
+        "@codemirror/lang-css": "^6.0.0",
+        "@codemirror/lang-javascript": "^6.0.0",
+        "@codemirror/language": "^6.4.0",
+        "@codemirror/state": "^6.0.0",
+        "@codemirror/view": "^6.2.2",
+        "@lezer/common": "^1.0.0",
+        "@lezer/css": "^1.1.0",
+        "@lezer/html": "^1.3.0"
+      }
+    },
+    "node_modules/@codemirror/lang-javascript": {
+      "version": "6.1.6",
+      "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.1.6.tgz",
+      "integrity": "sha512-TTK28z+vJQY9GAefLTDDptI2LMqMfAiuTpt8s9SsNwocjVQ1v9yTzfReMf1hYhspQCdhfa7fdKnQJ78mKe/bHQ==",
+      "dependencies": {
+        "@codemirror/autocomplete": "^6.0.0",
+        "@codemirror/language": "^6.6.0",
+        "@codemirror/lint": "^6.0.0",
+        "@codemirror/state": "^6.0.0",
+        "@codemirror/view": "^6.0.0",
+        "@lezer/common": "^1.0.0",
+        "@lezer/javascript": "^1.0.0"
+      }
+    },
+    "node_modules/@codemirror/lang-json": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz",
+      "integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==",
+      "dependencies": {
+        "@codemirror/language": "^6.0.0",
+        "@lezer/json": "^1.0.0"
+      }
+    },
+    "node_modules/@codemirror/lang-markdown": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-6.1.1.tgz",
+      "integrity": "sha512-n87Ms6Y5UYb1UkFu8sRzTLfq/yyF1y2AYiWvaVdbBQi5WDj1tFk5N+AKA+WC0Jcjc1VxvrCCM0iizjdYYi9sFQ==",
+      "dependencies": {
+        "@codemirror/lang-html": "^6.0.0",
+        "@codemirror/language": "^6.3.0",
+        "@codemirror/state": "^6.0.0",
+        "@codemirror/view": "^6.0.0",
+        "@lezer/common": "^1.0.0",
+        "@lezer/markdown": "^1.0.0"
+      }
+    },
+    "node_modules/@codemirror/lang-php": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/@codemirror/lang-php/-/lang-php-6.0.1.tgz",
+      "integrity": "sha512-ublojMdw/PNWa7qdN5TMsjmqkNuTBD3k6ndZ4Z0S25SBAiweFGyY68AS3xNcIOlb6DDFDvKlinLQ40vSLqf8xA==",
+      "dependencies": {
+        "@codemirror/lang-html": "^6.0.0",
+        "@codemirror/language": "^6.0.0",
+        "@codemirror/state": "^6.0.0",
+        "@lezer/common": "^1.0.0",
+        "@lezer/php": "^1.0.0"
+      }
+    },
+    "node_modules/@codemirror/lang-xml": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.0.2.tgz",
+      "integrity": "sha512-JQYZjHL2LAfpiZI2/qZ/qzDuSqmGKMwyApYmEUUCTxLM4MWS7sATUEfIguZQr9Zjx/7gcdnewb039smF6nC2zw==",
+      "dependencies": {
+        "@codemirror/autocomplete": "^6.0.0",
+        "@codemirror/language": "^6.4.0",
+        "@codemirror/state": "^6.0.0",
+        "@lezer/common": "^1.0.0",
+        "@lezer/xml": "^1.0.0"
+      }
+    },
+    "node_modules/@codemirror/language": {
+      "version": "6.6.0",
+      "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.6.0.tgz",
+      "integrity": "sha512-cwUd6lzt3MfNYOobdjf14ZkLbJcnv4WtndYaoBkbor/vF+rCNguMPK0IRtvZJG4dsWiaWPcK8x1VijhvSxnstg==",
+      "dependencies": {
+        "@codemirror/state": "^6.0.0",
+        "@codemirror/view": "^6.0.0",
+        "@lezer/common": "^1.0.0",
+        "@lezer/highlight": "^1.0.0",
+        "@lezer/lr": "^1.0.0",
+        "style-mod": "^4.0.0"
+      }
+    },
+    "node_modules/@codemirror/legacy-modes": {
+      "version": "6.3.2",
+      "resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.3.2.tgz",
+      "integrity": "sha512-ki5sqNKWzKi5AKvpVE6Cna4Q+SgxYuYVLAZFSsMjGBWx5qSVa+D+xipix65GS3f2syTfAD9pXKMX4i4p49eneQ==",
+      "dependencies": {
+        "@codemirror/language": "^6.0.0"
+      }
+    },
+    "node_modules/@codemirror/lint": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.2.1.tgz",
+      "integrity": "sha512-y1muai5U/uUPAGRyHMx9mHuHLypPcHWxzlZGknp/U5Mdb5Ol8Q5ZLp67UqyTbNFJJ3unVxZ8iX3g1fMN79S1JQ==",
+      "dependencies": {
+        "@codemirror/state": "^6.0.0",
+        "@codemirror/view": "^6.0.0",
+        "crelt": "^1.0.5"
+      }
+    },
+    "node_modules/@codemirror/search": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.3.0.tgz",
+      "integrity": "sha512-rBhZxzT34CarfhgCZGhaLBScABDN3iqJxixzNuINp9lrb3lzm0nTpR77G1VrxGO3HOGK7j62jcJftQM7eCOIuw==",
+      "dependencies": {
+        "@codemirror/state": "^6.0.0",
+        "@codemirror/view": "^6.0.0",
+        "crelt": "^1.0.5"
+      }
+    },
+    "node_modules/@codemirror/state": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.2.0.tgz",
+      "integrity": "sha512-69QXtcrsc3RYtOtd+GsvczJ319udtBf1PTrr2KbLWM/e2CXUPnh0Nz9AUo8WfhSQ7GeL8dPVNUmhQVgpmuaNGA=="
+    },
+    "node_modules/@codemirror/theme-one-dark": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.1.tgz",
+      "integrity": "sha512-+CfzmScfJuD6uDF5bHJkAjWTQ2QAAHxODCPxUEgcImDYcJLT+4l5vLnBHmDVv46kCC5uUJGMrBJct2Z6JbvqyQ==",
+      "dependencies": {
+        "@codemirror/language": "^6.0.0",
+        "@codemirror/state": "^6.0.0",
+        "@codemirror/view": "^6.0.0",
+        "@lezer/highlight": "^1.0.0"
+      }
+    },
+    "node_modules/@codemirror/view": {
+      "version": "6.9.4",
+      "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.9.4.tgz",
+      "integrity": "sha512-Ov2H9gwlGUxiH94zWxlLtTlyogSFaQDIYjtSEcfzgh7MkKmKVchkmr4JbtR5zBev3jY5DVtKvUC8yjd1bKW55A==",
+      "dependencies": {
+        "@codemirror/state": "^6.1.4",
+        "style-mod": "^4.0.0",
+        "w3c-keyname": "^2.2.4"
       }
     },
     "node_modules/@esbuild/android-arm": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.3.tgz",
-      "integrity": "sha512-1Mlz934GvbgdDmt26rTLmf03cAgLg5HyOgJN+ZGCeP3Q9ynYTNMn2/LQxIl7Uy+o4K6Rfi2OuLsr12JQQR8gNg==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.17.tgz",
+      "integrity": "sha512-E6VAZwN7diCa3labs0GYvhEPL2M94WLF8A+czO8hfjREXxba8Ng7nM5VxV+9ihNXIY1iQO1XxUU4P7hbqbICxg==",
       "cpu": [
         "arm"
       ],
       }
     },
     "node_modules/@esbuild/android-arm64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.3.tgz",
-      "integrity": "sha512-XvJsYo3dO3Pi4kpalkyMvfQsjxPWHYjoX4MDiB/FUM4YMfWcXa5l4VCwFWVYI1+92yxqjuqrhNg0CZg3gSouyQ==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.17.tgz",
+      "integrity": "sha512-jaJ5IlmaDLFPNttv0ofcwy/cfeY4bh/n705Tgh+eLObbGtQBK3EPAu+CzL95JVE4nFAliyrnEu0d32Q5foavqg==",
       "cpu": [
         "arm64"
       ],
       }
     },
     "node_modules/@esbuild/android-x64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.3.tgz",
-      "integrity": "sha512-nuV2CmLS07Gqh5/GrZLuqkU9Bm6H6vcCspM+zjp9TdQlxJtIe+qqEXQChmfc7nWdyr/yz3h45Utk1tUn8Cz5+A==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.17.tgz",
+      "integrity": "sha512-446zpfJ3nioMC7ASvJB1pszHVskkw4u/9Eu8s5yvvsSDTzYh4p4ZIRj0DznSl3FBF0Z/mZfrKXTtt0QCoFmoHA==",
       "cpu": [
         "x64"
       ],
       }
     },
     "node_modules/@esbuild/darwin-arm64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.3.tgz",
-      "integrity": "sha512-01Hxaaat6m0Xp9AXGM8mjFtqqwDjzlMP0eQq9zll9U85ttVALGCGDuEvra5Feu/NbP5AEP1MaopPwzsTcUq1cw==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.17.tgz",
+      "integrity": "sha512-m/gwyiBwH3jqfUabtq3GH31otL/0sE0l34XKpSIqR7NjQ/XHQ3lpmQHLHbG8AHTGCw8Ao059GvV08MS0bhFIJQ==",
       "cpu": [
         "arm64"
       ],
       }
     },
     "node_modules/@esbuild/darwin-x64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.3.tgz",
-      "integrity": "sha512-Eo2gq0Q/er2muf8Z83X21UFoB7EU6/m3GNKvrhACJkjVThd0uA+8RfKpfNhuMCl1bKRfBzKOk6xaYKQZ4lZqvA==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.17.tgz",
+      "integrity": "sha512-4utIrsX9IykrqYaXR8ob9Ha2hAY2qLc6ohJ8c0CN1DR8yWeMrTgYFjgdeQ9LIoTOfLetXjuCu5TRPHT9yKYJVg==",
       "cpu": [
         "x64"
       ],
       }
     },
     "node_modules/@esbuild/freebsd-arm64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.3.tgz",
-      "integrity": "sha512-CN62ESxaquP61n1ZjQP/jZte8CE09M6kNn3baos2SeUfdVBkWN5n6vGp2iKyb/bm/x4JQzEvJgRHLGd5F5b81w==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.17.tgz",
+      "integrity": "sha512-4PxjQII/9ppOrpEwzQ1b0pXCsFLqy77i0GaHodrmzH9zq2/NEhHMAMJkJ635Ns4fyJPFOlHMz4AsklIyRqFZWA==",
       "cpu": [
         "arm64"
       ],
       }
     },
     "node_modules/@esbuild/freebsd-x64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.3.tgz",
-      "integrity": "sha512-feq+K8TxIznZE+zhdVurF3WNJ/Sa35dQNYbaqM/wsCbWdzXr5lyq+AaTUSER2cUR+SXPnd/EY75EPRjf4s1SLg==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.17.tgz",
+      "integrity": "sha512-lQRS+4sW5S3P1sv0z2Ym807qMDfkmdhUYX30GRBURtLTrJOPDpoU0kI6pVz1hz3U0+YQ0tXGS9YWveQjUewAJw==",
       "cpu": [
         "x64"
       ],
       }
     },
     "node_modules/@esbuild/linux-arm": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.3.tgz",
-      "integrity": "sha512-CLP3EgyNuPcg2cshbwkqYy5bbAgK+VhyfMU7oIYyn+x4Y67xb5C5ylxsNUjRmr8BX+MW3YhVNm6Lq6FKtRTWHQ==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.17.tgz",
+      "integrity": "sha512-biDs7bjGdOdcmIk6xU426VgdRUpGg39Yz6sT9Xp23aq+IEHDb/u5cbmu/pAANpDB4rZpY/2USPhCA+w9t3roQg==",
       "cpu": [
         "arm"
       ],
       }
     },
     "node_modules/@esbuild/linux-arm64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.3.tgz",
-      "integrity": "sha512-JHeZXD4auLYBnrKn6JYJ0o5nWJI9PhChA/Nt0G4MvLaMrvXuWnY93R3a7PiXeJQphpL1nYsaMcoV2QtuvRnF/g==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.17.tgz",
+      "integrity": "sha512-2+pwLx0whKY1/Vqt8lyzStyda1v0qjJ5INWIe+d8+1onqQxHLLi3yr5bAa4gvbzhZqBztifYEu8hh1La5+7sUw==",
       "cpu": [
         "arm64"
       ],
       }
     },
     "node_modules/@esbuild/linux-ia32": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.3.tgz",
-      "integrity": "sha512-FyXlD2ZjZqTFh0sOQxFDiWG1uQUEOLbEh9gKN/7pFxck5Vw0qjWSDqbn6C10GAa1rXJpwsntHcmLqydY9ST9ZA==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.17.tgz",
+      "integrity": "sha512-IBTTv8X60dYo6P2t23sSUYym8fGfMAiuv7PzJ+0LcdAndZRzvke+wTVxJeCq4WgjppkOpndL04gMZIFvwoU34Q==",
       "cpu": [
         "ia32"
       ],
       }
     },
     "node_modules/@esbuild/linux-loong64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.3.tgz",
-      "integrity": "sha512-OrDGMvDBI2g7s04J8dh8/I7eSO+/E7nMDT2Z5IruBfUO/RiigF1OF6xoH33Dn4W/OwAWSUf1s2nXamb28ZklTA==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.17.tgz",
+      "integrity": "sha512-WVMBtcDpATjaGfWfp6u9dANIqmU9r37SY8wgAivuKmgKHE+bWSuv0qXEFt/p3qXQYxJIGXQQv6hHcm7iWhWjiw==",
       "cpu": [
         "loong64"
       ],
       }
     },
     "node_modules/@esbuild/linux-mips64el": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.3.tgz",
-      "integrity": "sha512-DcnUpXnVCJvmv0TzuLwKBC2nsQHle8EIiAJiJ+PipEVC16wHXaPEKP0EqN8WnBe0TPvMITOUlP2aiL5YMld+CQ==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.17.tgz",
+      "integrity": "sha512-2kYCGh8589ZYnY031FgMLy0kmE4VoGdvfJkxLdxP4HJvWNXpyLhjOvxVsYjYZ6awqY4bgLR9tpdYyStgZZhi2A==",
       "cpu": [
         "mips64el"
       ],
       }
     },
     "node_modules/@esbuild/linux-ppc64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.3.tgz",
-      "integrity": "sha512-BDYf/l1WVhWE+FHAW3FzZPtVlk9QsrwsxGzABmN4g8bTjmhazsId3h127pliDRRu5674k1Y2RWejbpN46N9ZhQ==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.17.tgz",
+      "integrity": "sha512-KIdG5jdAEeAKogfyMTcszRxy3OPbZhq0PPsW4iKKcdlbk3YE4miKznxV2YOSmiK/hfOZ+lqHri3v8eecT2ATwQ==",
       "cpu": [
         "ppc64"
       ],
       }
     },
     "node_modules/@esbuild/linux-riscv64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.3.tgz",
-      "integrity": "sha512-WViAxWYMRIi+prTJTyV1wnqd2mS2cPqJlN85oscVhXdb/ZTFJdrpaqm/uDsZPGKHtbg5TuRX/ymKdOSk41YZow==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.17.tgz",
+      "integrity": "sha512-Cj6uWLBR5LWhcD/2Lkfg2NrkVsNb2sFM5aVEfumKB2vYetkA/9Uyc1jVoxLZ0a38sUhFk4JOVKH0aVdPbjZQeA==",
       "cpu": [
         "riscv64"
       ],
       }
     },
     "node_modules/@esbuild/linux-s390x": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.3.tgz",
-      "integrity": "sha512-Iw8lkNHUC4oGP1O/KhumcVy77u2s6+KUjieUqzEU3XuWJqZ+AY7uVMrrCbAiwWTkpQHkr00BuXH5RpC6Sb/7Ug==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.17.tgz",
+      "integrity": "sha512-lK+SffWIr0XsFf7E0srBjhpkdFVJf3HEgXCwzkm69kNbRar8MhezFpkIwpk0qo2IOQL4JE4mJPJI8AbRPLbuOQ==",
       "cpu": [
         "s390x"
       ],
       }
     },
     "node_modules/@esbuild/linux-x64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.3.tgz",
-      "integrity": "sha512-0AGkWQMzeoeAtXQRNB3s4J1/T2XbigM2/Mn2yU1tQSmQRmHIZdkGbVq2A3aDdNslPyhb9/lH0S5GMTZ4xsjBqg==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.17.tgz",
+      "integrity": "sha512-XcSGTQcWFQS2jx3lZtQi7cQmDYLrpLRyz1Ns1DzZCtn898cWfm5Icx/DEWNcTU+T+tyPV89RQtDnI7qL2PObPg==",
       "cpu": [
         "x64"
       ],
       }
     },
     "node_modules/@esbuild/netbsd-x64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.3.tgz",
-      "integrity": "sha512-4+rR/WHOxIVh53UIQIICryjdoKdHsFZFD4zLSonJ9RRw7bhKzVyXbnRPsWSfwybYqw9sB7ots/SYyufL1mBpEg==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.17.tgz",
+      "integrity": "sha512-RNLCDmLP5kCWAJR+ItLM3cHxzXRTe4N00TQyQiimq+lyqVqZWGPAvcyfUBM0isE79eEZhIuGN09rAz8EL5KdLA==",
       "cpu": [
         "x64"
       ],
       }
     },
     "node_modules/@esbuild/openbsd-x64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.3.tgz",
-      "integrity": "sha512-cVpWnkx9IYg99EjGxa5Gc0XmqumtAwK3aoz7O4Dii2vko+qXbkHoujWA68cqXjhh6TsLaQelfDO4MVnyr+ODeA==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.17.tgz",
+      "integrity": "sha512-PAXswI5+cQq3Pann7FNdcpSUrhrql3wKjj3gVkmuz6OHhqqYxKvi6GgRBoaHjaG22HV/ZZEgF9TlS+9ftHVigA==",
       "cpu": [
         "x64"
       ],
       }
     },
     "node_modules/@esbuild/sunos-x64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.3.tgz",
-      "integrity": "sha512-RxmhKLbTCDAY2xOfrww6ieIZkZF+KBqG7S2Ako2SljKXRFi+0863PspK74QQ7JpmWwncChY25JTJSbVBYGQk2Q==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.17.tgz",
+      "integrity": "sha512-V63egsWKnx/4V0FMYkr9NXWrKTB5qFftKGKuZKFIrAkO/7EWLFnbBZNM1CvJ6Sis+XBdPws2YQSHF1Gqf1oj/Q==",
       "cpu": [
         "x64"
       ],
       }
     },
     "node_modules/@esbuild/win32-arm64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.3.tgz",
-      "integrity": "sha512-0r36VeEJ4efwmofxVJRXDjVRP2jTmv877zc+i+Pc7MNsIr38NfsjkQj23AfF7l0WbB+RQ7VUb+LDiqC/KY/M/A==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.17.tgz",
+      "integrity": "sha512-YtUXLdVnd6YBSYlZODjWzH+KzbaubV0YVd6UxSfoFfa5PtNJNaW+1i+Hcmjpg2nEe0YXUCNF5bkKy1NnBv1y7Q==",
       "cpu": [
         "arm64"
       ],
       }
     },
     "node_modules/@esbuild/win32-ia32": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.3.tgz",
-      "integrity": "sha512-wgO6rc7uGStH22nur4aLFcq7Wh86bE9cOFmfTr/yxN3BXvDEdCSXyKkO+U5JIt53eTOgC47v9k/C1bITWL/Teg==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.17.tgz",
+      "integrity": "sha512-yczSLRbDdReCO74Yfc5tKG0izzm+lPMYyO1fFTcn0QNwnKmc3K+HdxZWLGKg4pZVte7XVgcFku7TIZNbWEJdeQ==",
       "cpu": [
         "ia32"
       ],
       }
     },
     "node_modules/@esbuild/win32-x64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.3.tgz",
-      "integrity": "sha512-FdVl64OIuiKjgXBjwZaJLKp0eaEckifbhn10dXWhysMJkWblg3OEEGKSIyhiD5RSgAya8WzP3DNkngtIg3Nt7g==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.17.tgz",
+      "integrity": "sha512-FNZw7H3aqhF9OyRQbDDnzUApDXfC1N6fgBhkqEO2jvYCJ+DxMTfZVqg3AX0R1khg1wHTBRD5SdcibSJ+XF6bFg==",
       "cpu": [
         "x64"
       ],
         "node": ">=12"
       }
     },
+    "node_modules/@eslint-community/eslint-utils": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+      "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+      "dev": true,
+      "dependencies": {
+        "eslint-visitor-keys": "^3.3.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "peerDependencies": {
+        "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+      }
+    },
+    "node_modules/@eslint-community/regexpp": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz",
+      "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==",
+      "dev": true,
+      "engines": {
+        "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+      }
+    },
+    "node_modules/@eslint/eslintrc": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz",
+      "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==",
+      "dev": true,
+      "dependencies": {
+        "ajv": "^6.12.4",
+        "debug": "^4.3.2",
+        "espree": "^9.5.1",
+        "globals": "^13.19.0",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^4.1.0",
+        "minimatch": "^3.1.2",
+        "strip-json-comments": "^3.1.1"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@eslint/js": {
+      "version": "8.38.0",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.38.0.tgz",
+      "integrity": "sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g==",
+      "dev": true,
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/@humanwhocodes/config-array": {
+      "version": "0.11.8",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
+      "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
+      "dev": true,
+      "dependencies": {
+        "@humanwhocodes/object-schema": "^1.2.1",
+        "debug": "^4.1.1",
+        "minimatch": "^3.0.5"
+      },
+      "engines": {
+        "node": ">=10.10.0"
+      }
+    },
+    "node_modules/@humanwhocodes/module-importer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+      "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+      "dev": true,
+      "engines": {
+        "node": ">=12.22"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
+    },
+    "node_modules/@humanwhocodes/object-schema": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+      "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+      "dev": true
+    },
+    "node_modules/@lezer/common": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.0.2.tgz",
+      "integrity": "sha512-SVgiGtMnMnW3ActR8SXgsDhw7a0w0ChHSYAyAUxxrOiJ1OqYWEKk/xJd84tTSPo1mo6DXLObAJALNnd0Hrv7Ng=="
+    },
+    "node_modules/@lezer/css": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.1.tgz",
+      "integrity": "sha512-mSjx+unLLapEqdOYDejnGBokB5+AiJKZVclmud0MKQOKx3DLJ5b5VTCstgDDknR6iIV4gVrN6euzsCnj0A2gQA==",
+      "dependencies": {
+        "@lezer/highlight": "^1.0.0",
+        "@lezer/lr": "^1.0.0"
+      }
+    },
+    "node_modules/@lezer/generator": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/@lezer/generator/-/generator-1.2.2.tgz",
+      "integrity": "sha512-O//eH9jTPM1GnbZruuD23xU68Pkuragonn1DEIom4Kt/eJN/QFt7Vzvp1YjV/XBmoUKC+2ySPgrA5fMF9FMM2g==",
+      "dev": true,
+      "dependencies": {
+        "@lezer/common": "^1.0.2",
+        "@lezer/lr": "^1.3.0"
+      },
+      "bin": {
+        "lezer-generator": "dist/lezer-generator.cjs"
+      }
+    },
+    "node_modules/@lezer/highlight": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.4.tgz",
+      "integrity": "sha512-IECkFmw2l7sFcYXrV8iT9GeY4W0fU4CxX0WMwhmhMIVjoDdD1Hr6q3G2NqVtLg/yVe5n7i4menG3tJ2r4eCrPQ==",
+      "dependencies": {
+        "@lezer/common": "^1.0.0"
+      }
+    },
+    "node_modules/@lezer/html": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.4.tgz",
+      "integrity": "sha512-HdJYMVZcT4YsMo7lW3ipL4NoyS2T67kMPuSVS5TgLGqmaCjEU/D6xv7zsa1ktvTK5lwk7zzF1e3eU6gBZIPm5g==",
+      "dependencies": {
+        "@lezer/common": "^1.0.0",
+        "@lezer/highlight": "^1.0.0",
+        "@lezer/lr": "^1.0.0"
+      }
+    },
+    "node_modules/@lezer/javascript": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.2.tgz",
+      "integrity": "sha512-77qdAD4zanmImPiAu4ibrMUzRc79UHoccdPa+Ey5iwS891TAkhnMAodUe17T7zV7tnF7e9HXM0pfmjoGEhrppg==",
+      "dependencies": {
+        "@lezer/highlight": "^1.1.3",
+        "@lezer/lr": "^1.3.0"
+      }
+    },
+    "node_modules/@lezer/json": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.0.tgz",
+      "integrity": "sha512-zbAuUY09RBzCoCA3lJ1+ypKw5WSNvLqGMtasdW6HvVOqZoCpPr8eWrsGnOVWGKGn8Rh21FnrKRVlJXrGAVUqRw==",
+      "dependencies": {
+        "@lezer/highlight": "^1.0.0",
+        "@lezer/lr": "^1.0.0"
+      }
+    },
+    "node_modules/@lezer/lr": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.3.tgz",
+      "integrity": "sha512-JPQe3mwJlzEVqy67iQiiGozhcngbO8QBgpqZM6oL1Wj/dXckrEexpBLeFkq0edtW5IqnPRFxA24BHJni8Js69w==",
+      "dependencies": {
+        "@lezer/common": "^1.0.0"
+      }
+    },
+    "node_modules/@lezer/markdown": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.0.2.tgz",
+      "integrity": "sha512-8CY0OoZ6V5EzPjSPeJ4KLVbtXdLBd8V6sRCooN5kHnO28ytreEGTyrtU/zUwo/XLRzGr/e1g44KlzKi3yWGB5A==",
+      "dependencies": {
+        "@lezer/common": "^1.0.0",
+        "@lezer/highlight": "^1.0.0"
+      }
+    },
+    "node_modules/@lezer/php": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@lezer/php/-/php-1.0.1.tgz",
+      "integrity": "sha512-aqdCQJOXJ66De22vzdwnuC502hIaG9EnPK2rSi+ebXyUd+j7GAX1mRjWZOVOmf3GST1YUfUCu6WXDiEgDGOVwA==",
+      "dependencies": {
+        "@lezer/highlight": "^1.0.0",
+        "@lezer/lr": "^1.1.0"
+      }
+    },
+    "node_modules/@lezer/xml": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.1.tgz",
+      "integrity": "sha512-jMDXrV953sDAUEMI25VNrI9dz94Ai96FfeglytFINhhwQ867HKlCE2jt3AwZTCT7M528WxdDWv/Ty8e9wizwmQ==",
+      "dependencies": {
+        "@lezer/highlight": "^1.0.0",
+        "@lezer/lr": "^1.0.0"
+      }
+    },
+    "node_modules/@nodelib/fs.scandir": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+      "dev": true,
+      "dependencies": {
+        "@nodelib/fs.stat": "2.0.5",
+        "run-parallel": "^1.1.9"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.stat": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+      "dev": true,
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.walk": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+      "dev": true,
+      "dependencies": {
+        "@nodelib/fs.scandir": "2.1.5",
+        "fastq": "^1.6.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@ssddanbrown/codemirror-lang-smarty": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@ssddanbrown/codemirror-lang-smarty/-/codemirror-lang-smarty-1.0.0.tgz",
+      "integrity": "sha512-F0ut1kmdbT3eORk3xVIKfQsGCZiQdh+6sLayBa0+FTex2gyIQlVQZRRA7bPSlchI3uZtWwNnqGNz5O/QLWRlFg=="
+    },
+    "node_modules/@ssddanbrown/codemirror-lang-twig": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@ssddanbrown/codemirror-lang-twig/-/codemirror-lang-twig-1.0.0.tgz",
+      "integrity": "sha512-7WIMIh8Ssc54TooGCY57WU2rKEqZZrcV2tZSVRPtd0gKYsrDEKCSLWpQjUWEx7bdgh3NKHUjq1O4ugIzI/+dwQ==",
+      "dependencies": {
+        "@codemirror/language": "^6.0.0",
+        "@lezer/highlight": "^1.0.0",
+        "@lezer/lr": "^1.0.0"
+      }
+    },
+    "node_modules/@types/json5": {
+      "version": "0.0.29",
+      "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+      "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+      "dev": true
+    },
+    "node_modules/acorn": {
+      "version": "8.8.2",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
+      "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
+      "dev": true,
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/acorn-jsx": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+      "dev": true,
+      "peerDependencies": {
+        "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      }
+    },
+    "node_modules/ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dev": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
     "node_modules/ansi-regex": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
       }
     },
     "node_modules/anymatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
-      "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
       "dev": true,
       "dependencies": {
         "normalize-path": "^3.0.0",
       "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
       "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
     },
+    "node_modules/array-buffer-byte-length": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz",
+      "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "is-array-buffer": "^3.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/array-includes": {
+      "version": "3.1.6",
+      "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz",
+      "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4",
+        "get-intrinsic": "^1.1.3",
+        "is-string": "^1.0.7"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/array.prototype.flat": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz",
+      "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4",
+        "es-shim-unscopables": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/array.prototype.flatmap": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz",
+      "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4",
+        "es-shim-unscopables": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/available-typed-arrays": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
+      "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/camelcase": {
       "version": "5.3.1",
       "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
         "node": ">= 8.10.0"
       }
     },
-    "node_modules/clipboard": {
-      "version": "2.0.11",
-      "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz",
-      "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
-      "dependencies": {
-        "good-listener": "^1.2.2",
-        "select": "^1.1.2",
-        "tiny-emitter": "^2.0.0"
-      }
-    },
     "node_modules/cliui": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
       }
     },
     "node_modules/codemirror": {
-      "version": "5.65.9",
-      "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.9.tgz",
-      "integrity": "sha512-19Jox5sAKpusTDgqgKB5dawPpQcY+ipQK7xoEI+MVucEF9qqFaXpeqY1KaoyGBso/wHQoDa4HMMxMjdsS3Zzzw=="
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
+      "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
+      "dependencies": {
+        "@codemirror/autocomplete": "^6.0.0",
+        "@codemirror/commands": "^6.0.0",
+        "@codemirror/language": "^6.0.0",
+        "@codemirror/lint": "^6.0.0",
+        "@codemirror/search": "^6.0.0",
+        "@codemirror/state": "^6.0.0",
+        "@codemirror/view": "^6.0.0"
+      }
     },
     "node_modules/color-convert": {
       "version": "1.9.3",
       "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
       "dev": true
     },
+    "node_modules/confusing-browser-globals": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz",
+      "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==",
+      "dev": true
+    },
+    "node_modules/crelt": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.5.tgz",
+      "integrity": "sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA=="
+    },
     "node_modules/cross-spawn": {
       "version": "6.0.5",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
         "node": ">=4.8"
       }
     },
-    "node_modules/decamelize": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
-      "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+    "node_modules/debug": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+      "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
       "dev": true,
+      "dependencies": {
+        "ms": "2.1.2"
+      },
       "engines": {
-        "node": ">=0.10.0"
-      }
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/deep-is": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+      "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+      "dev": true
     },
     "node_modules/define-properties": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
-      "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
+      "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
       "dev": true,
       "dependencies": {
         "has-property-descriptors": "^1.0.0",
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/delegate": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
-      "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+    "node_modules/doctrine": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+      "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+      "dev": true,
+      "dependencies": {
+        "esutils": "^2.0.2"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
     },
     "node_modules/dropzone": {
       "version": "5.9.3",
       }
     },
     "node_modules/es-abstract": {
-      "version": "1.20.4",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz",
-      "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==",
+      "version": "1.21.2",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz",
+      "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==",
       "dev": true,
       "dependencies": {
+        "array-buffer-byte-length": "^1.0.0",
+        "available-typed-arrays": "^1.0.5",
         "call-bind": "^1.0.2",
+        "es-set-tostringtag": "^2.0.1",
         "es-to-primitive": "^1.2.1",
-        "function-bind": "^1.1.1",
         "function.prototype.name": "^1.1.5",
-        "get-intrinsic": "^1.1.3",
+        "get-intrinsic": "^1.2.0",
         "get-symbol-description": "^1.0.0",
+        "globalthis": "^1.0.3",
+        "gopd": "^1.0.1",
         "has": "^1.0.3",
         "has-property-descriptors": "^1.0.0",
+        "has-proto": "^1.0.1",
         "has-symbols": "^1.0.3",
-        "internal-slot": "^1.0.3",
+        "internal-slot": "^1.0.5",
+        "is-array-buffer": "^3.0.2",
         "is-callable": "^1.2.7",
         "is-negative-zero": "^2.0.2",
         "is-regex": "^1.1.4",
         "is-shared-array-buffer": "^1.0.2",
         "is-string": "^1.0.7",
+        "is-typed-array": "^1.1.10",
         "is-weakref": "^1.0.2",
-        "object-inspect": "^1.12.2",
+        "object-inspect": "^1.12.3",
         "object-keys": "^1.1.1",
         "object.assign": "^4.1.4",
         "regexp.prototype.flags": "^1.4.3",
         "safe-regex-test": "^1.0.0",
-        "string.prototype.trimend": "^1.0.5",
-        "string.prototype.trimstart": "^1.0.5",
-        "unbox-primitive": "^1.0.2"
+        "string.prototype.trim": "^1.2.7",
+        "string.prototype.trimend": "^1.0.6",
+        "string.prototype.trimstart": "^1.0.6",
+        "typed-array-length": "^1.0.4",
+        "unbox-primitive": "^1.0.2",
+        "which-typed-array": "^1.1.9"
       },
       "engines": {
         "node": ">= 0.4"
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/es-set-tostringtag": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz",
+      "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==",
+      "dev": true,
+      "dependencies": {
+        "get-intrinsic": "^1.1.3",
+        "has": "^1.0.3",
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-shim-unscopables": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
+      "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==",
+      "dev": true,
+      "dependencies": {
+        "has": "^1.0.3"
+      }
+    },
     "node_modules/es-to-primitive": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
       }
     },
     "node_modules/esbuild": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.3.tgz",
-      "integrity": "sha512-9n3AsBRe6sIyOc6kmoXg2ypCLgf3eZSraWFRpnkto+svt8cZNuKTkb1bhQcitBcvIqjNiK7K0J3KPmwGSfkA8g==",
+      "version": "0.17.17",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.17.tgz",
+      "integrity": "sha512-/jUywtAymR8jR4qsa2RujlAF7Krpt5VWi72Q2yuLD4e/hvtNcFQ0I1j8m/bxq238pf3/0KO5yuXNpuLx8BE1KA==",
       "dev": true,
       "hasInstallScript": true,
       "bin": {
         "node": ">=12"
       },
       "optionalDependencies": {
-        "@esbuild/android-arm": "0.17.3",
-        "@esbuild/android-arm64": "0.17.3",
-        "@esbuild/android-x64": "0.17.3",
-        "@esbuild/darwin-arm64": "0.17.3",
-        "@esbuild/darwin-x64": "0.17.3",
-        "@esbuild/freebsd-arm64": "0.17.3",
-        "@esbuild/freebsd-x64": "0.17.3",
-        "@esbuild/linux-arm": "0.17.3",
-        "@esbuild/linux-arm64": "0.17.3",
-        "@esbuild/linux-ia32": "0.17.3",
-        "@esbuild/linux-loong64": "0.17.3",
-        "@esbuild/linux-mips64el": "0.17.3",
-        "@esbuild/linux-ppc64": "0.17.3",
-        "@esbuild/linux-riscv64": "0.17.3",
-        "@esbuild/linux-s390x": "0.17.3",
-        "@esbuild/linux-x64": "0.17.3",
-        "@esbuild/netbsd-x64": "0.17.3",
-        "@esbuild/openbsd-x64": "0.17.3",
-        "@esbuild/sunos-x64": "0.17.3",
-        "@esbuild/win32-arm64": "0.17.3",
-        "@esbuild/win32-ia32": "0.17.3",
-        "@esbuild/win32-x64": "0.17.3"
+        "@esbuild/android-arm": "0.17.17",
+        "@esbuild/android-arm64": "0.17.17",
+        "@esbuild/android-x64": "0.17.17",
+        "@esbuild/darwin-arm64": "0.17.17",
+        "@esbuild/darwin-x64": "0.17.17",
+        "@esbuild/freebsd-arm64": "0.17.17",
+        "@esbuild/freebsd-x64": "0.17.17",
+        "@esbuild/linux-arm": "0.17.17",
+        "@esbuild/linux-arm64": "0.17.17",
+        "@esbuild/linux-ia32": "0.17.17",
+        "@esbuild/linux-loong64": "0.17.17",
+        "@esbuild/linux-mips64el": "0.17.17",
+        "@esbuild/linux-ppc64": "0.17.17",
+        "@esbuild/linux-riscv64": "0.17.17",
+        "@esbuild/linux-s390x": "0.17.17",
+        "@esbuild/linux-x64": "0.17.17",
+        "@esbuild/netbsd-x64": "0.17.17",
+        "@esbuild/openbsd-x64": "0.17.17",
+        "@esbuild/sunos-x64": "0.17.17",
+        "@esbuild/win32-arm64": "0.17.17",
+        "@esbuild/win32-ia32": "0.17.17",
+        "@esbuild/win32-x64": "0.17.17"
       }
     },
     "node_modules/escape-string-regexp": {
         "node": ">=0.8.0"
       }
     },
-    "node_modules/fill-range": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
-      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
-      "dev": true,
-      "dependencies": {
-        "to-regex-range": "^5.0.1"
+    "node_modules/eslint": {
+      "version": "8.38.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.38.0.tgz",
+      "integrity": "sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg==",
+      "dev": true,
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.2.0",
+        "@eslint-community/regexpp": "^4.4.0",
+        "@eslint/eslintrc": "^2.0.2",
+        "@eslint/js": "8.38.0",
+        "@humanwhocodes/config-array": "^0.11.8",
+        "@humanwhocodes/module-importer": "^1.0.1",
+        "@nodelib/fs.walk": "^1.2.8",
+        "ajv": "^6.10.0",
+        "chalk": "^4.0.0",
+        "cross-spawn": "^7.0.2",
+        "debug": "^4.3.2",
+        "doctrine": "^3.0.0",
+        "escape-string-regexp": "^4.0.0",
+        "eslint-scope": "^7.1.1",
+        "eslint-visitor-keys": "^3.4.0",
+        "espree": "^9.5.1",
+        "esquery": "^1.4.2",
+        "esutils": "^2.0.2",
+        "fast-deep-equal": "^3.1.3",
+        "file-entry-cache": "^6.0.1",
+        "find-up": "^5.0.0",
+        "glob-parent": "^6.0.2",
+        "globals": "^13.19.0",
+        "grapheme-splitter": "^1.0.4",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.0.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "is-path-inside": "^3.0.3",
+        "js-sdsl": "^4.1.4",
+        "js-yaml": "^4.1.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "levn": "^0.4.1",
+        "lodash.merge": "^4.6.2",
+        "minimatch": "^3.1.2",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.1",
+        "strip-ansi": "^6.0.1",
+        "strip-json-comments": "^3.1.0",
+        "text-table": "^0.2.0"
+      },
+      "bin": {
+        "eslint": "bin/eslint.js"
       },
       "engines": {
-        "node": ">=8"
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
       }
     },
-    "node_modules/find-up": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
-      "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+    "node_modules/eslint-config-airbnb-base": {
+      "version": "15.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz",
+      "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==",
       "dev": true,
       "dependencies": {
-        "locate-path": "^3.0.0"
+        "confusing-browser-globals": "^1.0.10",
+        "object.assign": "^4.1.2",
+        "object.entries": "^1.1.5",
+        "semver": "^6.3.0"
       },
       "engines": {
-        "node": ">=6"
+        "node": "^10.12.0 || >=12.0.0"
+      },
+      "peerDependencies": {
+        "eslint": "^7.32.0 || ^8.2.0",
+        "eslint-plugin-import": "^2.25.2"
       }
     },
-    "node_modules/fsevents": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
-      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+    "node_modules/eslint-config-airbnb-base/node_modules/semver": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+      "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
       "dev": true,
-      "hasInstallScript": true,
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      "bin": {
+        "semver": "bin/semver.js"
       }
     },
-    "node_modules/function-bind": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
-      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
-      "dev": true
-    },
-    "node_modules/function.prototype.name": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
-      "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
+    "node_modules/eslint-import-resolver-node": {
+      "version": "0.3.7",
+      "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz",
+      "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.1.3",
-        "es-abstract": "^1.19.0",
-        "functions-have-names": "^1.2.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
+        "debug": "^3.2.7",
+        "is-core-module": "^2.11.0",
+        "resolve": "^1.22.1"
       }
     },
-    "node_modules/functions-have-names": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
-      "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+    "node_modules/eslint-import-resolver-node/node_modules/debug": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
       "dev": true,
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
+      "dependencies": {
+        "ms": "^2.1.1"
       }
     },
-    "node_modules/get-caller-file": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
-      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+    "node_modules/eslint-module-utils": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz",
+      "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==",
       "dev": true,
+      "dependencies": {
+        "debug": "^3.2.7"
+      },
       "engines": {
-        "node": "6.* || 8.* || >= 10.*"
+        "node": ">=4"
+      },
+      "peerDependenciesMeta": {
+        "eslint": {
+          "optional": true
+        }
       }
     },
-    "node_modules/get-intrinsic": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
-      "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
+    "node_modules/eslint-module-utils/node_modules/debug": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
       "dev": true,
       "dependencies": {
-        "function-bind": "^1.1.1",
-        "has": "^1.0.3",
-        "has-symbols": "^1.0.3"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
+        "ms": "^2.1.1"
       }
     },
-    "node_modules/get-symbol-description": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
-      "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
+    "node_modules/eslint-plugin-import": {
+      "version": "2.27.5",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz",
+      "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.2",
-        "get-intrinsic": "^1.1.1"
+        "array-includes": "^3.1.6",
+        "array.prototype.flat": "^1.3.1",
+        "array.prototype.flatmap": "^1.3.1",
+        "debug": "^3.2.7",
+        "doctrine": "^2.1.0",
+        "eslint-import-resolver-node": "^0.3.7",
+        "eslint-module-utils": "^2.7.4",
+        "has": "^1.0.3",
+        "is-core-module": "^2.11.0",
+        "is-glob": "^4.0.3",
+        "minimatch": "^3.1.2",
+        "object.values": "^1.1.6",
+        "resolve": "^1.22.1",
+        "semver": "^6.3.0",
+        "tsconfig-paths": "^3.14.1"
       },
       "engines": {
-        "node": ">= 0.4"
+        "node": ">=4"
       },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
+      "peerDependencies": {
+        "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8"
       }
     },
-    "node_modules/glob-parent": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
-      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+    "node_modules/eslint-plugin-import/node_modules/debug": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
       "dev": true,
       "dependencies": {
-        "is-glob": "^4.0.1"
-      },
-      "engines": {
-        "node": ">= 6"
+        "ms": "^2.1.1"
       }
     },
-    "node_modules/good-listener": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
-      "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
+    "node_modules/eslint-plugin-import/node_modules/doctrine": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+      "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+      "dev": true,
       "dependencies": {
-        "delegate": "^3.1.2"
+        "esutils": "^2.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
       }
     },
-    "node_modules/graceful-fs": {
-      "version": "4.2.10",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
-      "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
-      "dev": true
+    "node_modules/eslint-plugin-import/node_modules/semver": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+      "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      }
     },
-    "node_modules/has": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
-      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+    "node_modules/eslint-scope": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz",
+      "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==",
       "dev": true,
       "dependencies": {
-        "function-bind": "^1.1.1"
+        "esrecurse": "^4.3.0",
+        "estraverse": "^5.2.0"
       },
       "engines": {
-        "node": ">= 0.4.0"
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
       }
     },
-    "node_modules/has-bigints": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
-      "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
+    "node_modules/eslint-visitor-keys": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz",
+      "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==",
       "dev": true,
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
       "funding": {
-        "url": "https://github.com/sponsors/ljharb"
+        "url": "https://opencollective.com/eslint"
       }
     },
-    "node_modules/has-flag": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+    "node_modules/eslint/node_modules/ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
       "dev": true,
       "engines": {
-        "node": ">=4"
+        "node": ">=8"
       }
     },
-    "node_modules/has-property-descriptors": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
-      "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
+    "node_modules/eslint/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
       "dev": true,
       "dependencies": {
-        "get-intrinsic": "^1.1.1"
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
       },
       "funding": {
-        "url": "https://github.com/sponsors/ljharb"
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
       }
     },
-    "node_modules/has-symbols": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
-      "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+    "node_modules/eslint/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
       "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
       "engines": {
-        "node": ">= 0.4"
+        "node": ">=10"
       },
       "funding": {
-        "url": "https://github.com/sponsors/ljharb"
+        "url": "https://github.com/chalk/chalk?sponsor=1"
       }
     },
-    "node_modules/has-tostringtag": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
-      "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+    "node_modules/eslint/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
       "dev": true,
       "dependencies": {
-        "has-symbols": "^1.0.2"
+        "color-name": "~1.1.4"
       },
       "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
+        "node": ">=7.0.0"
       }
     },
-    "node_modules/hosted-git-info": {
-      "version": "2.8.9",
-      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
-      "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
-      "dev": true
-    },
-    "node_modules/immutable": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz",
-      "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==",
+    "node_modules/eslint/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
       "dev": true
     },
-    "node_modules/internal-slot": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz",
-      "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==",
+    "node_modules/eslint/node_modules/cross-spawn": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+      "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
       "dev": true,
       "dependencies": {
-        "get-intrinsic": "^1.1.0",
-        "has": "^1.0.3",
-        "side-channel": "^1.0.4"
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
       },
       "engines": {
-        "node": ">= 0.4"
+        "node": ">= 8"
       }
     },
-    "node_modules/is-arrayish": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
-      "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
-      "dev": true
-    },
-    "node_modules/is-bigint": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
-      "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
+    "node_modules/eslint/node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
       "dev": true,
-      "dependencies": {
-        "has-bigints": "^1.0.1"
+      "engines": {
+        "node": ">=10"
       },
       "funding": {
-        "url": "https://github.com/sponsors/ljharb"
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/is-binary-path": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
-      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+    "node_modules/eslint/node_modules/find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
       "dev": true,
       "dependencies": {
-        "binary-extensions": "^2.0.0"
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/is-boolean-object": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
-      "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
+    "node_modules/eslint/node_modules/glob-parent": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+      "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
       "dev": true,
       "dependencies": {
-        "call-bind": "^1.0.2",
-        "has-tostringtag": "^1.0.0"
+        "is-glob": "^4.0.3"
       },
       "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
+        "node": ">=10.13.0"
       }
     },
-    "node_modules/is-callable": {
-      "version": "1.2.7",
-      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
-      "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+    "node_modules/eslint/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
       "dev": true,
       "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-core-module": {
-      "version": "2.11.0",
-      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
-      "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
-      "dev": true,
-      "dependencies": {
-        "has": "^1.0.3"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
+        "node": ">=8"
       }
     },
-    "node_modules/is-date-object": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
-      "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
+    "node_modules/eslint/node_modules/locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
       "dev": true,
       "dependencies": {
-        "has-tostringtag": "^1.0.0"
+        "p-locate": "^5.0.0"
       },
       "engines": {
-        "node": ">= 0.4"
+        "node": ">=10"
       },
       "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-extglob": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/is-fullwidth-code-point": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-      "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
-      "dev": true,
-      "engines": {
-        "node": ">=4"
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/is-glob": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
-      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+    "node_modules/eslint/node_modules/p-limit": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
       "dev": true,
       "dependencies": {
-        "is-extglob": "^2.1.1"
+        "yocto-queue": "^0.1.0"
       },
       "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/is-negative-zero": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
-      "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.4"
+        "node": ">=10"
       },
       "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-number": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
-      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.12.0"
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/is-number-object": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
-      "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
+    "node_modules/eslint/node_modules/p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
       "dev": true,
       "dependencies": {
-        "has-tostringtag": "^1.0.0"
+        "p-limit": "^3.0.2"
       },
       "engines": {
-        "node": ">= 0.4"
+        "node": ">=10"
       },
       "funding": {
-        "url": "https://github.com/sponsors/ljharb"
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/is-regex": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
-      "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+    "node_modules/eslint/node_modules/path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
       "dev": true,
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "has-tostringtag": "^1.0.0"
-      },
       "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-shared-array-buffer": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
-      "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
-      "dev": true,
-      "dependencies": {
-        "call-bind": "^1.0.2"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
+        "node": ">=8"
       }
     },
-    "node_modules/is-string": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
-      "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
+    "node_modules/eslint/node_modules/path-key": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
       "dev": true,
-      "dependencies": {
-        "has-tostringtag": "^1.0.0"
-      },
       "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
+        "node": ">=8"
       }
     },
-    "node_modules/is-symbol": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
-      "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
+    "node_modules/eslint/node_modules/shebang-command": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
       "dev": true,
       "dependencies": {
-        "has-symbols": "^1.0.2"
+        "shebang-regex": "^3.0.0"
       },
       "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-weakref": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
-      "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
-      "dev": true,
-      "dependencies": {
-        "call-bind": "^1.0.2"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/isexe": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
-      "dev": true
-    },
-    "node_modules/json-parse-better-errors": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
-      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
-      "dev": true
-    },
-    "node_modules/linkify-it": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz",
-      "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==",
-      "dependencies": {
-        "uc.micro": "^1.0.1"
+        "node": ">=8"
       }
     },
-    "node_modules/livereload": {
-      "version": "0.9.3",
-      "resolved": "https://registry.npmjs.org/livereload/-/livereload-0.9.3.tgz",
-      "integrity": "sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw==",
+    "node_modules/eslint/node_modules/shebang-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
       "dev": true,
-      "dependencies": {
-        "chokidar": "^3.5.0",
-        "livereload-js": "^3.3.1",
-        "opts": ">= 1.2.0",
-        "ws": "^7.4.3"
-      },
-      "bin": {
-        "livereload": "bin/livereload.js"
-      },
       "engines": {
-        "node": ">=8.0.0"
+        "node": ">=8"
       }
     },
-    "node_modules/livereload-js": {
-      "version": "3.4.1",
-      "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-3.4.1.tgz",
-      "integrity": "sha512-5MP0uUeVCec89ZbNOT/i97Mc+q3SxXmiUGhRFOTmhrGPn//uWVQdCvcLJDy64MSBR5MidFdOR7B9viumoavy6g==",
-      "dev": true
-    },
-    "node_modules/load-json-file": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
-      "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==",
+    "node_modules/eslint/node_modules/strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
       "dev": true,
       "dependencies": {
-        "graceful-fs": "^4.1.2",
-        "parse-json": "^4.0.0",
-        "pify": "^3.0.0",
-        "strip-bom": "^3.0.0"
+        "ansi-regex": "^5.0.1"
       },
       "engines": {
-        "node": ">=4"
+        "node": ">=8"
       }
     },
-    "node_modules/locate-path": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
-      "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+    "node_modules/eslint/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
       "dev": true,
       "dependencies": {
-        "p-locate": "^3.0.0",
-        "path-exists": "^3.0.0"
+        "has-flag": "^4.0.0"
       },
       "engines": {
-        "node": ">=6"
+        "node": ">=8"
       }
     },
-    "node_modules/lodash.debounce": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
-      "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
-      "dev": true
-    },
-    "node_modules/lodash.throttle": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
-      "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==",
-      "dev": true
-    },
-    "node_modules/markdown-it": {
-      "version": "13.0.1",
-      "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz",
-      "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==",
+    "node_modules/eslint/node_modules/which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "dev": true,
       "dependencies": {
-        "argparse": "^2.0.1",
-        "entities": "~3.0.1",
-        "linkify-it": "^4.0.1",
-        "mdurl": "^1.0.1",
-        "uc.micro": "^1.0.5"
+        "isexe": "^2.0.0"
       },
       "bin": {
-        "markdown-it": "bin/markdown-it.js"
-      }
-    },
-    "node_modules/markdown-it-task-lists": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/markdown-it-task-lists/-/markdown-it-task-lists-2.1.1.tgz",
-      "integrity": "sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA=="
-    },
-    "node_modules/mdurl": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
-      "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g=="
-    },
-    "node_modules/memorystream": {
-      "version": "0.3.1",
-      "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
-      "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==",
-      "dev": true,
+        "node-which": "bin/node-which"
+      },
       "engines": {
-        "node": ">= 0.10.0"
+        "node": ">= 8"
       }
     },
-    "node_modules/minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+    "node_modules/espree": {
+      "version": "9.5.1",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz",
+      "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==",
       "dev": true,
       "dependencies": {
-        "brace-expansion": "^1.1.7"
-      },
-      "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/nice-try": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
-      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
-      "dev": true
-    },
-    "node_modules/normalize-package-data": {
-      "version": "2.5.0",
-      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
-      "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
-      "dev": true,
-      "dependencies": {
-        "hosted-git-info": "^2.1.4",
-        "resolve": "^1.10.0",
-        "semver": "2 || 3 || 4 || 5",
-        "validate-npm-package-license": "^3.0.1"
-      }
-    },
-    "node_modules/normalize-path": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
-      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/npm-run-all": {
-      "version": "4.1.5",
-      "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
-      "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==",
-      "dev": true,
-      "dependencies": {
-        "ansi-styles": "^3.2.1",
-        "chalk": "^2.4.1",
-        "cross-spawn": "^6.0.5",
-        "memorystream": "^0.3.1",
-        "minimatch": "^3.0.4",
-        "pidtree": "^0.3.0",
-        "read-pkg": "^3.0.0",
-        "shell-quote": "^1.6.1",
-        "string.prototype.padend": "^3.0.0"
-      },
-      "bin": {
-        "npm-run-all": "bin/npm-run-all/index.js",
-        "run-p": "bin/run-p/index.js",
-        "run-s": "bin/run-s/index.js"
-      },
-      "engines": {
-        "node": ">= 4"
-      }
-    },
-    "node_modules/object-inspect": {
-      "version": "1.12.2",
-      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
-      "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
-      "dev": true,
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/object-keys": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
-      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/object.assign": {
-      "version": "4.1.4",
-      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
-      "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
-      "dev": true,
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.1.4",
-        "has-symbols": "^1.0.3",
-        "object-keys": "^1.1.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/opts": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/opts/-/opts-2.0.2.tgz",
-      "integrity": "sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==",
-      "dev": true
-    },
-    "node_modules/p-limit": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
-      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
-      "dev": true,
-      "dependencies": {
-        "p-try": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/p-locate": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
-      "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
-      "dev": true,
-      "dependencies": {
-        "p-limit": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/p-try": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
-      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/parse-json": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
-      "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==",
-      "dev": true,
-      "dependencies": {
-        "error-ex": "^1.3.1",
-        "json-parse-better-errors": "^1.0.1"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/path-exists": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-      "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/path-key": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
-      "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==",
-      "dev": true,
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/path-parse": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
-      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
-      "dev": true
-    },
-    "node_modules/path-type": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
-      "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
-      "dev": true,
-      "dependencies": {
-        "pify": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/picomatch": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
-      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
-      "dev": true,
-      "engines": {
-        "node": ">=8.6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/jonschlinkert"
-      }
-    },
-    "node_modules/pidtree": {
-      "version": "0.3.1",
-      "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz",
-      "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==",
-      "dev": true,
-      "bin": {
-        "pidtree": "bin/pidtree.js"
-      },
-      "engines": {
-        "node": ">=0.10"
-      }
-    },
-    "node_modules/pify": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-      "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
-      "dev": true,
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/punycode": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
-      "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
-      "dev": true,
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/read-pkg": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
-      "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==",
-      "dev": true,
-      "dependencies": {
-        "load-json-file": "^4.0.0",
-        "normalize-package-data": "^2.3.2",
-        "path-type": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/readdirp": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
-      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
-      "dev": true,
-      "dependencies": {
-        "picomatch": "^2.2.1"
-      },
-      "engines": {
-        "node": ">=8.10.0"
-      }
-    },
-    "node_modules/regexp.prototype.flags": {
-      "version": "1.4.3",
-      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
-      "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
-      "dev": true,
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.1.3",
-        "functions-have-names": "^1.2.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/require-directory": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
-      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/require-main-filename": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
-      "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
-      "dev": true
-    },
-    "node_modules/resolve": {
-      "version": "1.22.1",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
-      "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
-      "dev": true,
-      "dependencies": {
-        "is-core-module": "^2.9.0",
-        "path-parse": "^1.0.7",
-        "supports-preserve-symlinks-flag": "^1.0.0"
-      },
-      "bin": {
-        "resolve": "bin/resolve"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/safe-regex-test": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
-      "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==",
-      "dev": true,
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "get-intrinsic": "^1.1.3",
-        "is-regex": "^1.1.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/sass": {
-      "version": "1.57.1",
-      "resolved": "https://registry.npmjs.org/sass/-/sass-1.57.1.tgz",
-      "integrity": "sha512-O2+LwLS79op7GI0xZ8fqzF7X2m/m8WFfI02dHOdsK5R2ECeS5F62zrwg/relM1rjSLy7Vd/DiMNIvPrQGsA0jw==",
-      "dev": true,
-      "dependencies": {
-        "chokidar": ">=3.0.0 <4.0.0",
-        "immutable": "^4.0.0",
-        "source-map-js": ">=0.6.2 <2.0.0"
-      },
-      "bin": {
-        "sass": "sass.js"
-      },
-      "engines": {
-        "node": ">=12.0.0"
-      }
-    },
-    "node_modules/select": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
-      "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
-    },
-    "node_modules/semver": {
-      "version": "5.7.1",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
-      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
-      "dev": true,
-      "bin": {
-        "semver": "bin/semver"
-      }
-    },
-    "node_modules/set-blocking": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-      "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
-      "dev": true
-    },
-    "node_modules/shebang-command": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
-      "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
-      "dev": true,
-      "dependencies": {
-        "shebang-regex": "^1.0.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/shebang-regex": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
-      "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/shell-quote": {
-      "version": "1.7.4",
-      "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz",
-      "integrity": "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==",
-      "dev": true,
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/side-channel": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
-      "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
-      "dev": true,
-      "dependencies": {
-        "call-bind": "^1.0.0",
-        "get-intrinsic": "^1.0.2",
-        "object-inspect": "^1.9.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/snabbdom": {
-      "version": "3.5.1",
-      "resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-3.5.1.tgz",
-      "integrity": "sha512-wHMNIOjkm/YNE5EM3RCbr/+DVgPg6AqQAX1eOxO46zYNvCXjKP5Y865tqQj3EXnaMBjkxmQA5jFuDpDK/dbfiA==",
-      "engines": {
-        "node": ">=8.3.0"
-      }
-    },
-    "node_modules/sortablejs": {
-      "version": "1.15.0",
-      "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.0.tgz",
-      "integrity": "sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w=="
-    },
-    "node_modules/source-map-js": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
-      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/spdx-correct": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
-      "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
-      "dev": true,
-      "dependencies": {
-        "spdx-expression-parse": "^3.0.0",
-        "spdx-license-ids": "^3.0.0"
-      }
-    },
-    "node_modules/spdx-exceptions": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
-      "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
-      "dev": true
-    },
-    "node_modules/spdx-expression-parse": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
-      "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
-      "dev": true,
-      "dependencies": {
-        "spdx-exceptions": "^2.1.0",
-        "spdx-license-ids": "^3.0.0"
-      }
-    },
-    "node_modules/spdx-license-ids": {
-      "version": "3.0.12",
-      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz",
-      "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==",
-      "dev": true
-    },
-    "node_modules/string-width": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
-      "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
-      "dev": true,
-      "dependencies": {
-        "emoji-regex": "^7.0.1",
-        "is-fullwidth-code-point": "^2.0.0",
-        "strip-ansi": "^5.1.0"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/string.prototype.padend": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz",
-      "integrity": "sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==",
-      "dev": true,
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.1.3",
-        "es-abstract": "^1.19.1"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/string.prototype.trimend": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz",
-      "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==",
-      "dev": true,
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.1.4",
-        "es-abstract": "^1.19.5"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/string.prototype.trimstart": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz",
-      "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==",
-      "dev": true,
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "define-properties": "^1.1.4",
-        "es-abstract": "^1.19.5"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/strip-ansi": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
-      "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
-      "dev": true,
-      "dependencies": {
-        "ansi-regex": "^4.1.0"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/strip-bom": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
-      "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
-      "dev": true,
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/supports-color": {
-      "version": "5.5.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-      "dev": true,
-      "dependencies": {
-        "has-flag": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/supports-preserve-symlinks-flag": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
-      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/tiny-emitter": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
-      "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
-    },
-    "node_modules/to-regex-range": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
-      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
-      "dev": true,
-      "dependencies": {
-        "is-number": "^7.0.0"
-      },
-      "engines": {
-        "node": ">=8.0"
-      }
-    },
-    "node_modules/uc.micro": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
-      "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
-    },
-    "node_modules/unbox-primitive": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
-      "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
-      "dev": true,
-      "dependencies": {
-        "call-bind": "^1.0.2",
-        "has-bigints": "^1.0.2",
-        "has-symbols": "^1.0.3",
-        "which-boxed-primitive": "^1.0.2"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/validate-npm-package-license": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
-      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
-      "dev": true,
-      "dependencies": {
-        "spdx-correct": "^3.0.0",
-        "spdx-expression-parse": "^3.0.0"
-      }
-    },
-    "node_modules/which": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
-      "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
-      "dev": true,
-      "dependencies": {
-        "isexe": "^2.0.0"
-      },
-      "bin": {
-        "which": "bin/which"
-      }
-    },
-    "node_modules/which-boxed-primitive": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
-      "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
-      "dev": true,
-      "dependencies": {
-        "is-bigint": "^1.0.1",
-        "is-boolean-object": "^1.1.0",
-        "is-number-object": "^1.0.4",
-        "is-string": "^1.0.5",
-        "is-symbol": "^1.0.3"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/which-module": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
-      "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==",
-      "dev": true
-    },
-    "node_modules/wrap-ansi": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
-      "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
-      "dev": true,
-      "dependencies": {
-        "ansi-styles": "^3.2.0",
-        "string-width": "^3.0.0",
-        "strip-ansi": "^5.0.0"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/ws": {
-      "version": "7.5.9",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
-      "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
-      "dev": true,
-      "engines": {
-        "node": ">=8.3.0"
-      },
-      "peerDependencies": {
-        "bufferutil": "^4.0.1",
-        "utf-8-validate": "^5.0.2"
-      },
-      "peerDependenciesMeta": {
-        "bufferutil": {
-          "optional": true
-        },
-        "utf-8-validate": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/y18n": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
-      "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
-      "dev": true
-    },
-    "node_modules/yargs": {
-      "version": "13.3.2",
-      "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
-      "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
-      "dev": true,
-      "dependencies": {
-        "cliui": "^5.0.0",
-        "find-up": "^3.0.0",
-        "get-caller-file": "^2.0.1",
-        "require-directory": "^2.1.1",
-        "require-main-filename": "^2.0.0",
-        "set-blocking": "^2.0.0",
-        "string-width": "^3.0.0",
-        "which-module": "^2.0.0",
-        "y18n": "^4.0.0",
-        "yargs-parser": "^13.1.2"
-      }
-    },
-    "node_modules/yargs-parser": {
-      "version": "13.1.2",
-      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
-      "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
-      "dev": true,
-      "dependencies": {
-        "camelcase": "^5.0.0",
-        "decamelize": "^1.2.0"
-      }
-    }
-  },
-  "dependencies": {
-    "@esbuild/android-arm": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.3.tgz",
-      "integrity": "sha512-1Mlz934GvbgdDmt26rTLmf03cAgLg5HyOgJN+ZGCeP3Q9ynYTNMn2/LQxIl7Uy+o4K6Rfi2OuLsr12JQQR8gNg==",
-      "dev": true,
-      "optional": true
-    },
-    "@esbuild/android-arm64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.3.tgz",
-      "integrity": "sha512-XvJsYo3dO3Pi4kpalkyMvfQsjxPWHYjoX4MDiB/FUM4YMfWcXa5l4VCwFWVYI1+92yxqjuqrhNg0CZg3gSouyQ==",
-      "dev": true,
-      "optional": true
-    },
-    "@esbuild/android-x64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.3.tgz",
-      "integrity": "sha512-nuV2CmLS07Gqh5/GrZLuqkU9Bm6H6vcCspM+zjp9TdQlxJtIe+qqEXQChmfc7nWdyr/yz3h45Utk1tUn8Cz5+A==",
-      "dev": true,
-      "optional": true
-    },
-    "@esbuild/darwin-arm64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.3.tgz",
-      "integrity": "sha512-01Hxaaat6m0Xp9AXGM8mjFtqqwDjzlMP0eQq9zll9U85ttVALGCGDuEvra5Feu/NbP5AEP1MaopPwzsTcUq1cw==",
-      "dev": true,
-      "optional": true
-    },
-    "@esbuild/darwin-x64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.3.tgz",
-      "integrity": "sha512-Eo2gq0Q/er2muf8Z83X21UFoB7EU6/m3GNKvrhACJkjVThd0uA+8RfKpfNhuMCl1bKRfBzKOk6xaYKQZ4lZqvA==",
-      "dev": true,
-      "optional": true
-    },
-    "@esbuild/freebsd-arm64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.3.tgz",
-      "integrity": "sha512-CN62ESxaquP61n1ZjQP/jZte8CE09M6kNn3baos2SeUfdVBkWN5n6vGp2iKyb/bm/x4JQzEvJgRHLGd5F5b81w==",
-      "dev": true,
-      "optional": true
-    },
-    "@esbuild/freebsd-x64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.3.tgz",
-      "integrity": "sha512-feq+K8TxIznZE+zhdVurF3WNJ/Sa35dQNYbaqM/wsCbWdzXr5lyq+AaTUSER2cUR+SXPnd/EY75EPRjf4s1SLg==",
-      "dev": true,
-      "optional": true
-    },
-    "@esbuild/linux-arm": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.3.tgz",
-      "integrity": "sha512-CLP3EgyNuPcg2cshbwkqYy5bbAgK+VhyfMU7oIYyn+x4Y67xb5C5ylxsNUjRmr8BX+MW3YhVNm6Lq6FKtRTWHQ==",
-      "dev": true,
-      "optional": true
-    },
-    "@esbuild/linux-arm64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.3.tgz",
-      "integrity": "sha512-JHeZXD4auLYBnrKn6JYJ0o5nWJI9PhChA/Nt0G4MvLaMrvXuWnY93R3a7PiXeJQphpL1nYsaMcoV2QtuvRnF/g==",
-      "dev": true,
-      "optional": true
-    },
-    "@esbuild/linux-ia32": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.3.tgz",
-      "integrity": "sha512-FyXlD2ZjZqTFh0sOQxFDiWG1uQUEOLbEh9gKN/7pFxck5Vw0qjWSDqbn6C10GAa1rXJpwsntHcmLqydY9ST9ZA==",
-      "dev": true,
-      "optional": true
-    },
-    "@esbuild/linux-loong64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.3.tgz",
-      "integrity": "sha512-OrDGMvDBI2g7s04J8dh8/I7eSO+/E7nMDT2Z5IruBfUO/RiigF1OF6xoH33Dn4W/OwAWSUf1s2nXamb28ZklTA==",
-      "dev": true,
-      "optional": true
-    },
-    "@esbuild/linux-mips64el": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.3.tgz",
-      "integrity": "sha512-DcnUpXnVCJvmv0TzuLwKBC2nsQHle8EIiAJiJ+PipEVC16wHXaPEKP0EqN8WnBe0TPvMITOUlP2aiL5YMld+CQ==",
-      "dev": true,
-      "optional": true
-    },
-    "@esbuild/linux-ppc64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.3.tgz",
-      "integrity": "sha512-BDYf/l1WVhWE+FHAW3FzZPtVlk9QsrwsxGzABmN4g8bTjmhazsId3h127pliDRRu5674k1Y2RWejbpN46N9ZhQ==",
-      "dev": true,
-      "optional": true
-    },
-    "@esbuild/linux-riscv64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.3.tgz",
-      "integrity": "sha512-WViAxWYMRIi+prTJTyV1wnqd2mS2cPqJlN85oscVhXdb/ZTFJdrpaqm/uDsZPGKHtbg5TuRX/ymKdOSk41YZow==",
-      "dev": true,
-      "optional": true
-    },
-    "@esbuild/linux-s390x": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.3.tgz",
-      "integrity": "sha512-Iw8lkNHUC4oGP1O/KhumcVy77u2s6+KUjieUqzEU3XuWJqZ+AY7uVMrrCbAiwWTkpQHkr00BuXH5RpC6Sb/7Ug==",
-      "dev": true,
-      "optional": true
-    },
-    "@esbuild/linux-x64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.3.tgz",
-      "integrity": "sha512-0AGkWQMzeoeAtXQRNB3s4J1/T2XbigM2/Mn2yU1tQSmQRmHIZdkGbVq2A3aDdNslPyhb9/lH0S5GMTZ4xsjBqg==",
-      "dev": true,
-      "optional": true
-    },
-    "@esbuild/netbsd-x64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.3.tgz",
-      "integrity": "sha512-4+rR/WHOxIVh53UIQIICryjdoKdHsFZFD4zLSonJ9RRw7bhKzVyXbnRPsWSfwybYqw9sB7ots/SYyufL1mBpEg==",
-      "dev": true,
-      "optional": true
-    },
-    "@esbuild/openbsd-x64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.3.tgz",
-      "integrity": "sha512-cVpWnkx9IYg99EjGxa5Gc0XmqumtAwK3aoz7O4Dii2vko+qXbkHoujWA68cqXjhh6TsLaQelfDO4MVnyr+ODeA==",
-      "dev": true,
-      "optional": true
-    },
-    "@esbuild/sunos-x64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.3.tgz",
-      "integrity": "sha512-RxmhKLbTCDAY2xOfrww6ieIZkZF+KBqG7S2Ako2SljKXRFi+0863PspK74QQ7JpmWwncChY25JTJSbVBYGQk2Q==",
-      "dev": true,
-      "optional": true
-    },
-    "@esbuild/win32-arm64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.3.tgz",
-      "integrity": "sha512-0r36VeEJ4efwmofxVJRXDjVRP2jTmv877zc+i+Pc7MNsIr38NfsjkQj23AfF7l0WbB+RQ7VUb+LDiqC/KY/M/A==",
-      "dev": true,
-      "optional": true
-    },
-    "@esbuild/win32-ia32": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.3.tgz",
-      "integrity": "sha512-wgO6rc7uGStH22nur4aLFcq7Wh86bE9cOFmfTr/yxN3BXvDEdCSXyKkO+U5JIt53eTOgC47v9k/C1bITWL/Teg==",
-      "dev": true,
-      "optional": true
-    },
-    "@esbuild/win32-x64": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.3.tgz",
-      "integrity": "sha512-FdVl64OIuiKjgXBjwZaJLKp0eaEckifbhn10dXWhysMJkWblg3OEEGKSIyhiD5RSgAya8WzP3DNkngtIg3Nt7g==",
-      "dev": true,
-      "optional": true
-    },
-    "ansi-regex": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
-      "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
-      "dev": true
-    },
-    "ansi-styles": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-      "dev": true,
-      "requires": {
-        "color-convert": "^1.9.0"
-      }
-    },
-    "anymatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
-      "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
-      "dev": true,
-      "requires": {
-        "normalize-path": "^3.0.0",
-        "picomatch": "^2.0.4"
-      }
-    },
-    "argparse": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
-      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
-    },
-    "balanced-match": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
-      "dev": true
-    },
-    "binary-extensions": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
-      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
-      "dev": true
-    },
-    "brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
-      "requires": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "braces": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
-      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
-      "dev": true,
-      "requires": {
-        "fill-range": "^7.0.1"
-      }
-    },
-    "call-bind": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
-      "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
-      "dev": true,
-      "requires": {
-        "function-bind": "^1.1.1",
-        "get-intrinsic": "^1.0.2"
-      }
-    },
-    "camelcase": {
-      "version": "5.3.1",
-      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
-      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
-      "dev": true
-    },
-    "chalk": {
-      "version": "2.4.2",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
-      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-      "dev": true,
-      "requires": {
-        "ansi-styles": "^3.2.1",
-        "escape-string-regexp": "^1.0.5",
-        "supports-color": "^5.3.0"
-      }
-    },
-    "chokidar": {
-      "version": "3.5.3",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
-      "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
-      "dev": true,
-      "requires": {
-        "anymatch": "~3.1.2",
-        "braces": "~3.0.2",
-        "fsevents": "~2.3.2",
-        "glob-parent": "~5.1.2",
-        "is-binary-path": "~2.1.0",
-        "is-glob": "~4.0.1",
-        "normalize-path": "~3.0.0",
-        "readdirp": "~3.6.0"
-      }
-    },
-    "chokidar-cli": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/chokidar-cli/-/chokidar-cli-3.0.0.tgz",
-      "integrity": "sha512-xVW+Qeh7z15uZRxHOkP93Ux8A0xbPzwK4GaqD8dQOYc34TlkqUhVSS59fK36DOp5WdJlrRzlYSy02Ht99FjZqQ==",
-      "dev": true,
-      "requires": {
-        "chokidar": "^3.5.2",
-        "lodash.debounce": "^4.0.8",
-        "lodash.throttle": "^4.1.1",
-        "yargs": "^13.3.0"
-      }
-    },
-    "clipboard": {
-      "version": "2.0.11",
-      "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz",
-      "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
-      "requires": {
-        "good-listener": "^1.2.2",
-        "select": "^1.1.2",
-        "tiny-emitter": "^2.0.0"
+        "acorn": "^8.8.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^3.4.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
       }
     },
-    "cliui": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
-      "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+    "node_modules/esquery": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+      "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
       "dev": true,
-      "requires": {
-        "string-width": "^3.1.0",
-        "strip-ansi": "^5.2.0",
-        "wrap-ansi": "^5.1.0"
+      "dependencies": {
+        "estraverse": "^5.1.0"
+      },
+      "engines": {
+        "node": ">=0.10"
       }
     },
-    "codemirror": {
-      "version": "5.65.9",
-      "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.9.tgz",
-      "integrity": "sha512-19Jox5sAKpusTDgqgKB5dawPpQcY+ipQK7xoEI+MVucEF9qqFaXpeqY1KaoyGBso/wHQoDa4HMMxMjdsS3Zzzw=="
-    },
-    "color-convert": {
-      "version": "1.9.3",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+    "node_modules/esrecurse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
       "dev": true,
-      "requires": {
-        "color-name": "1.1.3"
+      "dependencies": {
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=4.0"
       }
     },
-    "color-name": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
-      "dev": true
-    },
-    "concat-map": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
-      "dev": true
-    },
-    "cross-spawn": {
-      "version": "6.0.5",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
-      "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+    "node_modules/estraverse": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
       "dev": true,
-      "requires": {
-        "nice-try": "^1.0.4",
-        "path-key": "^2.0.1",
-        "semver": "^5.5.0",
-        "shebang-command": "^1.2.0",
-        "which": "^1.2.9"
+      "engines": {
+        "node": ">=4.0"
       }
     },
-    "decamelize": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
-      "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
-      "dev": true
-    },
-    "define-properties": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
-      "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
+    "node_modules/esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
       "dev": true,
-      "requires": {
-        "has-property-descriptors": "^1.0.0",
-        "object-keys": "^1.1.1"
+      "engines": {
+        "node": ">=0.10.0"
       }
     },
-    "delegate": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
-      "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
-    },
-    "dropzone": {
-      "version": "5.9.3",
-      "resolved": "https://registry.npmjs.org/dropzone/-/dropzone-5.9.3.tgz",
-      "integrity": "sha512-Azk8kD/2/nJIuVPK+zQ9sjKMRIpRvNyqn9XwbBHNq+iNuSccbJS6hwm1Woy0pMST0erSo0u4j+KJaodndDk4vA=="
-    },
-    "emoji-regex": {
-      "version": "7.0.3",
-      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
-      "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+    "node_modules/fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
       "dev": true
     },
-    "entities": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
-      "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q=="
+    "node_modules/fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "dev": true
     },
-    "error-ex": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
-      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
-      "dev": true,
-      "requires": {
-        "is-arrayish": "^0.2.1"
-      }
+    "node_modules/fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+      "dev": true
     },
-    "es-abstract": {
-      "version": "1.20.4",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz",
-      "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==",
+    "node_modules/fastq": {
+      "version": "1.15.0",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
+      "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
       "dev": true,
-      "requires": {
-        "call-bind": "^1.0.2",
-        "es-to-primitive": "^1.2.1",
-        "function-bind": "^1.1.1",
-        "function.prototype.name": "^1.1.5",
-        "get-intrinsic": "^1.1.3",
-        "get-symbol-description": "^1.0.0",
-        "has": "^1.0.3",
-        "has-property-descriptors": "^1.0.0",
-        "has-symbols": "^1.0.3",
-        "internal-slot": "^1.0.3",
-        "is-callable": "^1.2.7",
-        "is-negative-zero": "^2.0.2",
-        "is-regex": "^1.1.4",
-        "is-shared-array-buffer": "^1.0.2",
-        "is-string": "^1.0.7",
-        "is-weakref": "^1.0.2",
-        "object-inspect": "^1.12.2",
-        "object-keys": "^1.1.1",
-        "object.assign": "^4.1.4",
-        "regexp.prototype.flags": "^1.4.3",
-        "safe-regex-test": "^1.0.0",
-        "string.prototype.trimend": "^1.0.5",
-        "string.prototype.trimstart": "^1.0.5",
-        "unbox-primitive": "^1.0.2"
+      "dependencies": {
+        "reusify": "^1.0.4"
       }
     },
-    "es-to-primitive": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
-      "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+    "node_modules/file-entry-cache": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+      "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
       "dev": true,
-      "requires": {
-        "is-callable": "^1.1.4",
-        "is-date-object": "^1.0.1",
-        "is-symbol": "^1.0.2"
+      "dependencies": {
+        "flat-cache": "^3.0.4"
+      },
+      "engines": {
+        "node": "^10.12.0 || >=12.0.0"
       }
     },
-    "esbuild": {
-      "version": "0.17.3",
-      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.3.tgz",
-      "integrity": "sha512-9n3AsBRe6sIyOc6kmoXg2ypCLgf3eZSraWFRpnkto+svt8cZNuKTkb1bhQcitBcvIqjNiK7K0J3KPmwGSfkA8g==",
-      "dev": true,
-      "requires": {
-        "@esbuild/android-arm": "0.17.3",
-        "@esbuild/android-arm64": "0.17.3",
-        "@esbuild/android-x64": "0.17.3",
-        "@esbuild/darwin-arm64": "0.17.3",
-        "@esbuild/darwin-x64": "0.17.3",
-        "@esbuild/freebsd-arm64": "0.17.3",
-        "@esbuild/freebsd-x64": "0.17.3",
-        "@esbuild/linux-arm": "0.17.3",
-        "@esbuild/linux-arm64": "0.17.3",
-        "@esbuild/linux-ia32": "0.17.3",
-        "@esbuild/linux-loong64": "0.17.3",
-        "@esbuild/linux-mips64el": "0.17.3",
-        "@esbuild/linux-ppc64": "0.17.3",
-        "@esbuild/linux-riscv64": "0.17.3",
-        "@esbuild/linux-s390x": "0.17.3",
-        "@esbuild/linux-x64": "0.17.3",
-        "@esbuild/netbsd-x64": "0.17.3",
-        "@esbuild/openbsd-x64": "0.17.3",
-        "@esbuild/sunos-x64": "0.17.3",
-        "@esbuild/win32-arm64": "0.17.3",
-        "@esbuild/win32-ia32": "0.17.3",
-        "@esbuild/win32-x64": "0.17.3"
-      }
-    },
-    "escape-string-regexp": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
-      "dev": true
-    },
-    "fill-range": {
+    "node_modules/fill-range": {
       "version": "7.0.1",
       "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
       "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "to-regex-range": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
       }
     },
-    "find-up": {
+    "node_modules/find-up": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
       "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "locate-path": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/flat-cache": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+      "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+      "dev": true,
+      "dependencies": {
+        "flatted": "^3.1.0",
+        "rimraf": "^3.0.2"
+      },
+      "engines": {
+        "node": "^10.12.0 || >=12.0.0"
+      }
+    },
+    "node_modules/flatted": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
+      "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
+      "dev": true
+    },
+    "node_modules/for-each": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+      "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+      "dev": true,
+      "dependencies": {
+        "is-callable": "^1.1.3"
       }
     },
-    "fsevents": {
+    "node_modules/fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+      "dev": true
+    },
+    "node_modules/fsevents": {
       "version": "2.3.2",
       "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
       "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
       "dev": true,
-      "optional": true
+      "hasInstallScript": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
     },
-    "function-bind": {
+    "node_modules/function-bind": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
       "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
       "dev": true
     },
-    "function.prototype.name": {
+    "node_modules/function.prototype.name": {
       "version": "1.1.5",
       "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
       "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "call-bind": "^1.0.2",
         "define-properties": "^1.1.3",
         "es-abstract": "^1.19.0",
         "functions-have-names": "^1.2.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "functions-have-names": {
+    "node_modules/functions-have-names": {
       "version": "1.2.3",
       "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
       "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
-      "dev": true
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
     },
-    "get-caller-file": {
+    "node_modules/get-caller-file": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
       "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": "6.* || 8.* || >= 10.*"
+      }
     },
-    "get-intrinsic": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
-      "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
+    "node_modules/get-intrinsic": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
+      "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "function-bind": "^1.1.1",
         "has": "^1.0.3",
         "has-symbols": "^1.0.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "get-symbol-description": {
+    "node_modules/get-symbol-description": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
       "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "call-bind": "^1.0.2",
         "get-intrinsic": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/glob": {
+      "version": "7.2.3",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "dev": true,
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.1.1",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
       }
     },
-    "glob-parent": {
+    "node_modules/glob-parent": {
       "version": "5.1.2",
       "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
       "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
       }
     },
-    "good-listener": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
-      "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
-      "requires": {
-        "delegate": "^3.1.2"
+    "node_modules/globals": {
+      "version": "13.20.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
+      "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
+      "dev": true,
+      "dependencies": {
+        "type-fest": "^0.20.2"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/globalthis": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
+      "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==",
+      "dev": true,
+      "dependencies": {
+        "define-properties": "^1.1.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/gopd": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+      "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+      "dev": true,
+      "dependencies": {
+        "get-intrinsic": "^1.1.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "graceful-fs": {
-      "version": "4.2.10",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
-      "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
+    "node_modules/graceful-fs": {
+      "version": "4.2.11",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+      "dev": true
+    },
+    "node_modules/grapheme-splitter": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
+      "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
       "dev": true
     },
-    "has": {
+    "node_modules/has": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
       "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "function-bind": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
       }
     },
-    "has-bigints": {
+    "node_modules/has-bigints": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
       "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
-      "dev": true
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
     },
-    "has-flag": {
+    "node_modules/has-flag": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
       "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
     },
-    "has-property-descriptors": {
+    "node_modules/has-property-descriptors": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
       "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "get-intrinsic": "^1.1.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
+      "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "has-symbols": {
+    "node_modules/has-symbols": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
       "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
     },
-    "has-tostringtag": {
+    "node_modules/has-tostringtag": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
       "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "has-symbols": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "hosted-git-info": {
+    "node_modules/hosted-git-info": {
       "version": "2.8.9",
       "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
       "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
       "dev": true
     },
-    "immutable": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz",
-      "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==",
+    "node_modules/ignore": {
+      "version": "5.2.4",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
+      "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/immutable": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.0.tgz",
+      "integrity": "sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==",
       "dev": true
     },
-    "internal-slot": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz",
-      "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==",
+    "node_modules/import-fresh": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+      "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+      "dev": true,
+      "dependencies": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.8.19"
+      }
+    },
+    "node_modules/inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+      "dev": true,
+      "dependencies": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true
+    },
+    "node_modules/internal-slot": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
+      "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==",
       "dev": true,
-      "requires": {
-        "get-intrinsic": "^1.1.0",
+      "dependencies": {
+        "get-intrinsic": "^1.2.0",
         "has": "^1.0.3",
         "side-channel": "^1.0.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/is-array-buffer": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz",
+      "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "get-intrinsic": "^1.2.0",
+        "is-typed-array": "^1.1.10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "is-arrayish": {
+    "node_modules/is-arrayish": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
       "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
       "dev": true
     },
-    "is-bigint": {
+    "node_modules/is-bigint": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
       "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "has-bigints": "^1.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "is-binary-path": {
+    "node_modules/is-binary-path": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
       "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "binary-extensions": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
       }
     },
-    "is-boolean-object": {
+    "node_modules/is-boolean-object": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
       "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "call-bind": "^1.0.2",
         "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "is-callable": {
+    "node_modules/is-callable": {
       "version": "1.2.7",
       "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
       "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
     },
-    "is-core-module": {
-      "version": "2.11.0",
-      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
-      "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
+    "node_modules/is-core-module": {
+      "version": "2.12.0",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz",
+      "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "has": "^1.0.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "is-date-object": {
+    "node_modules/is-date-object": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
       "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "is-extglob": {
+    "node_modules/is-extglob": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
       "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
     },
-    "is-fullwidth-code-point": {
+    "node_modules/is-fullwidth-code-point": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
       "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
     },
-    "is-glob": {
+    "node_modules/is-glob": {
       "version": "4.0.3",
       "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
       "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
       }
     },
-    "is-negative-zero": {
+    "node_modules/is-negative-zero": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
       "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
     },
-    "is-number": {
+    "node_modules/is-number": {
       "version": "7.0.0",
       "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
       "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": ">=0.12.0"
+      }
     },
-    "is-number-object": {
+    "node_modules/is-number-object": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
       "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-path-inside": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+      "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
       }
     },
-    "is-regex": {
+    "node_modules/is-regex": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
       "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "call-bind": "^1.0.2",
         "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "is-shared-array-buffer": {
+    "node_modules/is-shared-array-buffer": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
       "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "call-bind": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "is-string": {
+    "node_modules/is-string": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
       "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "is-symbol": {
+    "node_modules/is-symbol": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
       "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "has-symbols": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "is-weakref": {
+    "node_modules/is-typed-array": {
+      "version": "1.1.10",
+      "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz",
+      "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==",
+      "dev": true,
+      "dependencies": {
+        "available-typed-arrays": "^1.0.5",
+        "call-bind": "^1.0.2",
+        "for-each": "^0.3.3",
+        "gopd": "^1.0.1",
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-weakref": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
       "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "call-bind": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "isexe": {
+    "node_modules/isexe": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
       "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
       "dev": true
     },
-    "json-parse-better-errors": {
+    "node_modules/js-sdsl": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz",
+      "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==",
+      "dev": true,
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/js-sdsl"
+      }
+    },
+    "node_modules/js-yaml": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+      "dev": true,
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/json-parse-better-errors": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
       "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
       "dev": true
     },
-    "linkify-it": {
+    "node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true
+    },
+    "node_modules/json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+      "dev": true
+    },
+    "node_modules/json5": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+      "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+      "dev": true,
+      "dependencies": {
+        "minimist": "^1.2.0"
+      },
+      "bin": {
+        "json5": "lib/cli.js"
+      }
+    },
+    "node_modules/levn": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+      "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+      "dev": true,
+      "dependencies": {
+        "prelude-ls": "^1.2.1",
+        "type-check": "~0.4.0"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/linkify-it": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz",
       "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==",
-      "requires": {
+      "dependencies": {
         "uc.micro": "^1.0.1"
       }
     },
-    "livereload": {
+    "node_modules/livereload": {
       "version": "0.9.3",
       "resolved": "https://registry.npmjs.org/livereload/-/livereload-0.9.3.tgz",
       "integrity": "sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "chokidar": "^3.5.0",
         "livereload-js": "^3.3.1",
         "opts": ">= 1.2.0",
         "ws": "^7.4.3"
+      },
+      "bin": {
+        "livereload": "bin/livereload.js"
+      },
+      "engines": {
+        "node": ">=8.0.0"
       }
     },
-    "livereload-js": {
+    "node_modules/livereload-js": {
       "version": "3.4.1",
       "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-3.4.1.tgz",
       "integrity": "sha512-5MP0uUeVCec89ZbNOT/i97Mc+q3SxXmiUGhRFOTmhrGPn//uWVQdCvcLJDy64MSBR5MidFdOR7B9viumoavy6g==",
       "dev": true
     },
-    "load-json-file": {
+    "node_modules/load-json-file": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
       "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "graceful-fs": "^4.1.2",
         "parse-json": "^4.0.0",
         "pify": "^3.0.0",
         "strip-bom": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
       }
     },
-    "locate-path": {
+    "node_modules/locate-path": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
       "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "p-locate": "^3.0.0",
         "path-exists": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
       }
     },
-    "lodash.debounce": {
+    "node_modules/lodash.debounce": {
       "version": "4.0.8",
       "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
       "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
       "dev": true
     },
-    "lodash.throttle": {
+    "node_modules/lodash.merge": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+      "dev": true
+    },
+    "node_modules/lodash.throttle": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
       "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==",
       "dev": true
     },
-    "markdown-it": {
+    "node_modules/markdown-it": {
       "version": "13.0.1",
       "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz",
       "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==",
-      "requires": {
+      "dependencies": {
         "argparse": "^2.0.1",
         "entities": "~3.0.1",
         "linkify-it": "^4.0.1",
         "mdurl": "^1.0.1",
         "uc.micro": "^1.0.5"
+      },
+      "bin": {
+        "markdown-it": "bin/markdown-it.js"
       }
     },
-    "markdown-it-task-lists": {
+    "node_modules/markdown-it-task-lists": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/markdown-it-task-lists/-/markdown-it-task-lists-2.1.1.tgz",
       "integrity": "sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA=="
     },
-    "mdurl": {
+    "node_modules/mdurl": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
       "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g=="
     },
-    "memorystream": {
+    "node_modules/memorystream": {
       "version": "0.3.1",
       "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
       "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": ">= 0.10.0"
+      }
     },
-    "minimatch": {
+    "node_modules/minimatch": {
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
       "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "nice-try": {
+    "node_modules/ms": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+      "dev": true
+    },
+    "node_modules/natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+      "dev": true
+    },
+    "node_modules/nice-try": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
       "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
       "dev": true
     },
-    "normalize-package-data": {
+    "node_modules/normalize-package-data": {
       "version": "2.5.0",
       "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
       "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "hosted-git-info": "^2.1.4",
         "resolve": "^1.10.0",
         "semver": "2 || 3 || 4 || 5",
         "validate-npm-package-license": "^3.0.1"
       }
     },
-    "normalize-path": {
+    "node_modules/normalize-path": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
       "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
     },
-    "npm-run-all": {
+    "node_modules/npm-run-all": {
       "version": "4.1.5",
       "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
       "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "ansi-styles": "^3.2.1",
         "chalk": "^2.4.1",
         "cross-spawn": "^6.0.5",
         "read-pkg": "^3.0.0",
         "shell-quote": "^1.6.1",
         "string.prototype.padend": "^3.0.0"
+      },
+      "bin": {
+        "npm-run-all": "bin/npm-run-all/index.js",
+        "run-p": "bin/run-p/index.js",
+        "run-s": "bin/run-s/index.js"
+      },
+      "engines": {
+        "node": ">= 4"
       }
     },
-    "object-inspect": {
-      "version": "1.12.2",
-      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
-      "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
-      "dev": true
+    "node_modules/object-inspect": {
+      "version": "1.12.3",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
+      "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
     },
-    "object-keys": {
+    "node_modules/object-keys": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
       "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      }
     },
-    "object.assign": {
+    "node_modules/object.assign": {
       "version": "4.1.4",
       "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
       "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "call-bind": "^1.0.2",
         "define-properties": "^1.1.4",
         "has-symbols": "^1.0.3",
         "object-keys": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/object.entries": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz",
+      "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/object.values": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz",
+      "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "opts": {
+    "node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "dev": true,
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/optionator": {
+      "version": "0.9.1",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+      "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+      "dev": true,
+      "dependencies": {
+        "deep-is": "^0.1.3",
+        "fast-levenshtein": "^2.0.6",
+        "levn": "^0.4.1",
+        "prelude-ls": "^1.2.1",
+        "type-check": "^0.4.0",
+        "word-wrap": "^1.2.3"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/opts": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/opts/-/opts-2.0.2.tgz",
       "integrity": "sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==",
       "dev": true
     },
-    "p-limit": {
+    "node_modules/p-limit": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
       "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "p-try": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "p-locate": {
+    "node_modules/p-locate": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
       "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "p-limit": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6"
       }
     },
-    "p-try": {
+    "node_modules/p-try": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
       "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+      "dev": true,
+      "dependencies": {
+        "callsites": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
     },
-    "parse-json": {
+    "node_modules/parse-json": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
       "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "error-ex": "^1.3.1",
         "json-parse-better-errors": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=4"
       }
     },
-    "path-exists": {
+    "node_modules/path-exists": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
       "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
     },
-    "path-key": {
+    "node_modules/path-key": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
       "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
     },
-    "path-parse": {
+    "node_modules/path-parse": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
       "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
       "dev": true
     },
-    "path-type": {
+    "node_modules/path-type": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
       "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "pify": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
       }
     },
-    "picomatch": {
+    "node_modules/picomatch": {
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
       "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
     },
-    "pidtree": {
+    "node_modules/pidtree": {
       "version": "0.3.1",
       "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz",
       "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==",
-      "dev": true
+      "dev": true,
+      "bin": {
+        "pidtree": "bin/pidtree.js"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
     },
-    "pify": {
+    "node_modules/pify": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
       "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/prelude-ls": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+      "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.8.0"
+      }
     },
-    "punycode": {
+    "node_modules/punycode": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
       "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/queue-microtask": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
     },
-    "read-pkg": {
+    "node_modules/read-pkg": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
       "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "load-json-file": "^4.0.0",
         "normalize-package-data": "^2.3.2",
         "path-type": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
       }
     },
-    "readdirp": {
+    "node_modules/readdirp": {
       "version": "3.6.0",
       "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
       "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "picomatch": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=8.10.0"
       }
     },
-    "regexp.prototype.flags": {
+    "node_modules/regexp.prototype.flags": {
       "version": "1.4.3",
       "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
       "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "call-bind": "^1.0.2",
         "define-properties": "^1.1.3",
         "functions-have-names": "^1.2.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "require-directory": {
+    "node_modules/require-directory": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
       "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
     },
-    "require-main-filename": {
+    "node_modules/require-main-filename": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
       "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
       "dev": true
     },
-    "resolve": {
-      "version": "1.22.1",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
-      "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
+    "node_modules/resolve": {
+      "version": "1.22.3",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.3.tgz",
+      "integrity": "sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw==",
       "dev": true,
-      "requires": {
-        "is-core-module": "^2.9.0",
+      "dependencies": {
+        "is-core-module": "^2.12.0",
         "path-parse": "^1.0.7",
         "supports-preserve-symlinks-flag": "^1.0.0"
+      },
+      "bin": {
+        "resolve": "bin/resolve"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/reusify": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+      "dev": true,
+      "engines": {
+        "iojs": ">=1.0.0",
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "dev": true,
+      "dependencies": {
+        "glob": "^7.1.3"
+      },
+      "bin": {
+        "rimraf": "bin.js"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/run-parallel": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "dependencies": {
+        "queue-microtask": "^1.2.2"
       }
     },
-    "safe-regex-test": {
+    "node_modules/safe-regex-test": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
       "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "call-bind": "^1.0.2",
         "get-intrinsic": "^1.1.3",
         "is-regex": "^1.1.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "sass": {
-      "version": "1.57.1",
-      "resolved": "https://registry.npmjs.org/sass/-/sass-1.57.1.tgz",
-      "integrity": "sha512-O2+LwLS79op7GI0xZ8fqzF7X2m/m8WFfI02dHOdsK5R2ECeS5F62zrwg/relM1rjSLy7Vd/DiMNIvPrQGsA0jw==",
+    "node_modules/sass": {
+      "version": "1.62.0",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.62.0.tgz",
+      "integrity": "sha512-Q4USplo4pLYgCi+XlipZCWUQz5pkg/ruSSgJ0WRDSb/+3z9tXUOkQ7QPYn4XrhZKYAK4HlpaQecRwKLJX6+DBg==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "chokidar": ">=3.0.0 <4.0.0",
         "immutable": "^4.0.0",
         "source-map-js": ">=0.6.2 <2.0.0"
+      },
+      "bin": {
+        "sass": "sass.js"
+      },
+      "engines": {
+        "node": ">=14.0.0"
       }
     },
-    "select": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
-      "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
-    },
-    "semver": {
+    "node_modules/semver": {
       "version": "5.7.1",
       "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
       "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
-      "dev": true
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver"
+      }
     },
-    "set-blocking": {
+    "node_modules/set-blocking": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
       "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
       "dev": true
     },
-    "shebang-command": {
+    "node_modules/shebang-command": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
       "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "shebang-regex": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
       }
     },
-    "shebang-regex": {
+    "node_modules/shebang-regex": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
       "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
     },
-    "shell-quote": {
-      "version": "1.7.4",
-      "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz",
-      "integrity": "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==",
-      "dev": true
+    "node_modules/shell-quote": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz",
+      "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
     },
-    "side-channel": {
+    "node_modules/side-channel": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
       "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "call-bind": "^1.0.0",
         "get-intrinsic": "^1.0.2",
         "object-inspect": "^1.9.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "snabbdom": {
+    "node_modules/snabbdom": {
       "version": "3.5.1",
       "resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-3.5.1.tgz",
-      "integrity": "sha512-wHMNIOjkm/YNE5EM3RCbr/+DVgPg6AqQAX1eOxO46zYNvCXjKP5Y865tqQj3EXnaMBjkxmQA5jFuDpDK/dbfiA=="
+      "integrity": "sha512-wHMNIOjkm/YNE5EM3RCbr/+DVgPg6AqQAX1eOxO46zYNvCXjKP5Y865tqQj3EXnaMBjkxmQA5jFuDpDK/dbfiA==",
+      "engines": {
+        "node": ">=8.3.0"
+      }
     },
-    "sortablejs": {
+    "node_modules/sortablejs": {
       "version": "1.15.0",
       "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.0.tgz",
       "integrity": "sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w=="
     },
-    "source-map-js": {
+    "node_modules/source-map-js": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
       "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
     },
-    "spdx-correct": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
-      "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+    "node_modules/spdx-correct": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
+      "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "spdx-expression-parse": "^3.0.0",
         "spdx-license-ids": "^3.0.0"
       }
     },
-    "spdx-exceptions": {
+    "node_modules/spdx-exceptions": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
       "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
       "dev": true
     },
-    "spdx-expression-parse": {
+    "node_modules/spdx-expression-parse": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
       "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "spdx-exceptions": "^2.1.0",
         "spdx-license-ids": "^3.0.0"
       }
     },
-    "spdx-license-ids": {
-      "version": "3.0.12",
-      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz",
-      "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==",
+    "node_modules/spdx-license-ids": {
+      "version": "3.0.13",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz",
+      "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==",
       "dev": true
     },
-    "string-width": {
+    "node_modules/string-width": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
       "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "emoji-regex": "^7.0.1",
         "is-fullwidth-code-point": "^2.0.0",
         "strip-ansi": "^5.1.0"
+      },
+      "engines": {
+        "node": ">=6"
       }
     },
-    "string.prototype.padend": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz",
-      "integrity": "sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==",
+    "node_modules/string.prototype.padend": {
+      "version": "3.1.4",
+      "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.4.tgz",
+      "integrity": "sha512-67otBXoksdjsnXXRUq+KMVTdlVRZ2af422Y0aTyTjVaoQkGr3mxl2Bc5emi7dOQ3OGVVQQskmLEWwFXwommpNw==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "call-bind": "^1.0.2",
-        "define-properties": "^1.1.3",
-        "es-abstract": "^1.19.1"
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "string.prototype.trimend": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz",
-      "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==",
+    "node_modules/string.prototype.trim": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz",
+      "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "call-bind": "^1.0.2",
         "define-properties": "^1.1.4",
-        "es-abstract": "^1.19.5"
+        "es-abstract": "^1.20.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "string.prototype.trimstart": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz",
-      "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==",
+    "node_modules/string.prototype.trimend": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz",
+      "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/string.prototype.trimstart": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz",
+      "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "call-bind": "^1.0.2",
         "define-properties": "^1.1.4",
-        "es-abstract": "^1.19.5"
+        "es-abstract": "^1.20.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "strip-ansi": {
+    "node_modules/strip-ansi": {
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
       "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "ansi-regex": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=6"
       }
     },
-    "strip-bom": {
+    "node_modules/strip-bom": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
       "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/style-mod": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.0.3.tgz",
+      "integrity": "sha512-78Jv8kYJdjbvRwwijtCevYADfsI0lGzYJe4mMFdceO8l75DFFDoqBhR1jVDicDRRaX4//g1u9wKeo+ztc2h1Rw=="
     },
-    "supports-color": {
+    "node_modules/supports-color": {
       "version": "5.5.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
       "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "has-flag": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
       }
     },
-    "supports-preserve-symlinks-flag": {
+    "node_modules/supports-preserve-symlinks-flag": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
       "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
-      "dev": true
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
     },
-    "tiny-emitter": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
-      "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+    "node_modules/text-table": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+      "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+      "dev": true
     },
-    "to-regex-range": {
+    "node_modules/to-regex-range": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
       "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "is-number": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/tsconfig-paths": {
+      "version": "3.14.2",
+      "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz",
+      "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==",
+      "dev": true,
+      "dependencies": {
+        "@types/json5": "^0.0.29",
+        "json5": "^1.0.2",
+        "minimist": "^1.2.6",
+        "strip-bom": "^3.0.0"
+      }
+    },
+    "node_modules/type-check": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+      "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+      "dev": true,
+      "dependencies": {
+        "prelude-ls": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/type-fest": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+      "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/typed-array-length": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz",
+      "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "for-each": "^0.3.3",
+        "is-typed-array": "^1.1.9"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "uc.micro": {
+    "node_modules/uc.micro": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
       "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
     },
-    "unbox-primitive": {
+    "node_modules/unbox-primitive": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
       "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "call-bind": "^1.0.2",
         "has-bigints": "^1.0.2",
         "has-symbols": "^1.0.3",
         "which-boxed-primitive": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+      "dev": true,
+      "dependencies": {
+        "punycode": "^2.1.0"
       }
     },
-    "validate-npm-package-license": {
+    "node_modules/validate-npm-package-license": {
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
       "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "spdx-correct": "^3.0.0",
         "spdx-expression-parse": "^3.0.0"
       }
     },
-    "which": {
+    "node_modules/w3c-keyname": {
+      "version": "2.2.6",
+      "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.6.tgz",
+      "integrity": "sha512-f+fciywl1SJEniZHD6H+kUO8gOnwIr7f4ijKA6+ZvJFjeGi1r4PDLl53Ayud9O/rk64RqgoQine0feoeOU0kXg=="
+    },
+    "node_modules/which": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
       "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "isexe": "^2.0.0"
+      },
+      "bin": {
+        "which": "bin/which"
       }
     },
-    "which-boxed-primitive": {
+    "node_modules/which-boxed-primitive": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
       "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "is-bigint": "^1.0.1",
         "is-boolean-object": "^1.1.0",
         "is-number-object": "^1.0.4",
         "is-string": "^1.0.5",
         "is-symbol": "^1.0.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "which-module": {
+    "node_modules/which-module": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
       "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==",
       "dev": true
     },
-    "wrap-ansi": {
+    "node_modules/which-typed-array": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz",
+      "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==",
+      "dev": true,
+      "dependencies": {
+        "available-typed-arrays": "^1.0.5",
+        "call-bind": "^1.0.2",
+        "for-each": "^0.3.3",
+        "gopd": "^1.0.1",
+        "has-tostringtag": "^1.0.0",
+        "is-typed-array": "^1.1.10"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/word-wrap": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+      "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/wrap-ansi": {
       "version": "5.1.0",
       "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
       "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "ansi-styles": "^3.2.0",
         "string-width": "^3.0.0",
         "strip-ansi": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=6"
       }
     },
-    "ws": {
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+      "dev": true
+    },
+    "node_modules/ws": {
       "version": "7.5.9",
       "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
       "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
       "dev": true,
-      "requires": {}
+      "engines": {
+        "node": ">=8.3.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": "^5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
     },
-    "y18n": {
+    "node_modules/y18n": {
       "version": "4.0.3",
       "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
       "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
       "dev": true
     },
-    "yargs": {
+    "node_modules/yargs": {
       "version": "13.3.2",
       "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
       "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "cliui": "^5.0.0",
         "find-up": "^3.0.0",
         "get-caller-file": "^2.0.1",
         "yargs-parser": "^13.1.2"
       }
     },
-    "yargs-parser": {
+    "node_modules/yargs-parser": {
       "version": "13.1.2",
       "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
       "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "camelcase": "^5.0.0",
         "decamelize": "^1.2.0"
       }
+    },
+    "node_modules/yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
     }
   }
 }
index 89fb0749293a7b7a252ad78c8ec4453a58c032f2..39f0882340cdaecad47ef1c7daa4dea76896511e 100644 (file)
     "build:css:watch": "sass ./resources/sass:./public/dist --watch --embed-sources",
     "build:css:production": "sass ./resources/sass:./public/dist -s compressed",
     "build:js:dev": "node dev/build/esbuild.js",
-    "build:js:watch": "chokidar --initial \"./resources/**/*.js\" -c \"npm run build:js:dev\"",
+    "build:js:watch": "chokidar --initial \"./resources/**/*.js\" \"./resources/**/*.mjs\" -c \"npm run build:js:dev\"",
     "build:js:production": "node dev/build/esbuild.js production",
     "build": "npm-run-all --parallel build:*:dev",
     "production": "npm-run-all --parallel build:*:production",
     "dev": "npm-run-all --parallel watch livereload",
     "watch": "npm-run-all --parallel build:*:watch",
     "livereload": "livereload ./public/dist/",
-    "permissions": "chown -R $USER:$USER bootstrap/cache storage public/uploads"
+    "permissions": "chown -R $USER:$USER bootstrap/cache storage public/uploads",
+    "lint": "eslint \"resources/**/*.js\" \"resources/**/*.mjs\"",
+    "fix": "eslint --fix \"resources/**/*.js\" \"resources/**/*.mjs\""
   },
   "devDependencies": {
+    "@lezer/generator": "^1.2.2",
     "chokidar-cli": "^3.0",
-    "esbuild": "^0.17.3",
+    "esbuild": "^0.17.16",
+    "eslint": "^8.38.0",
+    "eslint-config-airbnb-base": "^15.0.0",
+    "eslint-plugin-import": "^2.27.5",
     "livereload": "^0.9.3",
     "npm-run-all": "^4.1.5",
     "punycode": "^2.3.0",
-    "sass": "^1.57.0"
+    "sass": "^1.62.0"
   },
   "dependencies": {
-    "clipboard": "^2.0.11",
-    "codemirror": "^5.65.5",
+    "@codemirror/commands": "^6.2.2",
+    "@codemirror/lang-css": "^6.1.1",
+    "@codemirror/lang-html": "^6.4.3",
+    "@codemirror/lang-javascript": "^6.1.6",
+    "@codemirror/lang-json": "^6.0.1",
+    "@codemirror/lang-markdown": "^6.1.1",
+    "@codemirror/lang-php": "^6.0.1",
+    "@codemirror/lang-xml": "^6.0.2",
+    "@codemirror/language": "^6.6.0",
+    "@codemirror/legacy-modes": "^6.3.2",
+    "@codemirror/state": "^6.2.0",
+    "@codemirror/theme-one-dark": "^6.1.1",
+    "@codemirror/view": "^6.9.4",
+    "@lezer/highlight": "^1.1.4",
+    "@ssddanbrown/codemirror-lang-smarty": "^1.0.0",
+    "@ssddanbrown/codemirror-lang-twig": "^1.0.0",
+    "codemirror": "^6.0.1",
     "dropzone": "^5.9.3",
     "markdown-it": "^13.0.1",
     "markdown-it-task-lists": "^2.1.1",
     "snabbdom": "^3.5.1",
     "sortablejs": "^1.15.0"
+  },
+  "eslintConfig": {
+    "root": true,
+    "env": {
+      "browser": true,
+      "es2021": true
+    },
+    "extends": "airbnb-base",
+    "ignorePatterns": [
+      "resources/**/*-stub.js"
+    ],
+    "overrides": [],
+    "parserOptions": {
+      "ecmaVersion": "latest",
+      "sourceType": "module"
+    },
+    "rules": {
+      "indent": [
+        "error",
+        4
+      ],
+      "arrow-parens": [
+        "error",
+        "as-needed"
+      ],
+      "padded-blocks": [
+        "error",
+        {
+          "blocks": "never",
+          "classes": "always"
+        }
+      ],
+      "object-curly-spacing": [
+        "error",
+        "never"
+      ],
+      "space-before-function-paren": [
+        "error",
+        {
+          "anonymous": "never",
+          "named": "never",
+          "asyncArrow": "always"
+        }
+      ],
+      "import/prefer-default-export": "off",
+      "no-plusplus": [
+        "error",
+        {
+          "allowForLoopAfterthoughts": true
+        }
+      ],
+      "arrow-body-style": "off",
+      "no-restricted-syntax": "off",
+      "no-continue": "off",
+      "prefer-destructuring": "off",
+      "class-methods-use-this": "off",
+      "no-param-reassign": "off",
+      "no-console": [
+        "warn",
+        {
+          "allow": [
+            "error",
+            "warn"
+          ]
+        }
+      ],
+      "no-new": "off",
+      "max-len": [
+        "error",
+        {
+          "code": 110,
+          "tabWidth": 4,
+          "ignoreUrls": true,
+          "ignoreComments": false,
+          "ignoreRegExpLiterals": true,
+          "ignoreStrings": true,
+          "ignoreTemplateLiterals": true
+        }
+      ]
+    }
   }
 }
index 28822dd8ec07abbf8af8513b8c0cdbce925d28a1..f56b2c2bd97853a763cbf85c4437e837f4b6e3f9 100644 (file)
--- a/readme.md
+++ b/readme.md
@@ -134,7 +134,6 @@ Note: This is not an exhaustive list of all libraries and projects that would be
 * [Sortable](https://github.com/SortableJS/Sortable) - _[MIT](https://github.com/SortableJS/Sortable/blob/master/LICENSE)_
 * [Google Material Icons](https://github.com/google/material-design-icons) - _[Apache-2.0](https://github.com/google/material-design-icons/blob/master/LICENSE)_
 * [Dropzone.js](http://www.dropzonejs.com/) - _[MIT](https://github.com/dropzone/dropzone/blob/main/LICENSE)_
-* [clipboard.js](https://clipboardjs.com/) - _[MIT](https://github.com/zenorocha/clipboard.js/blob/master/LICENSE)_
 * [markdown-it](https://github.com/markdown-it/markdown-it) and [markdown-it-task-lists](https://github.com/revin/markdown-it-task-lists) - _[MIT](https://github.com/markdown-it/markdown-it/blob/master/LICENSE) and [ISC](https://github.com/revin/markdown-it-task-lists/blob/master/LICENSE)_
 * [Dompdf](https://github.com/dompdf/dompdf) - _[LGPL v2.1](https://github.com/dompdf/dompdf/blob/master/LICENSE.LGPL)_
 * [BarryVD/Dompdf](https://github.com/barryvdh/laravel-dompdf) - _[MIT](https://github.com/barryvdh/laravel-dompdf/blob/master/LICENSE)_
index e49bf5e955bd040d1b68114fb17e8b307a3a024e..5b822e9009e26f58f4695215c4570c9a86bd59de 100644 (file)
@@ -1,34 +1,37 @@
+import * as events from './services/events';
+import * as httpInstance from './services/http';
+import Translations from './services/translations';
+
+import * as components from './services/components';
+import * as componentMap from './components';
+
 // Url retrieval function
-window.baseUrl = function(path) {
+window.baseUrl = function baseUrl(path) {
+    let targetPath = path;
     let basePath = document.querySelector('meta[name="base-url"]').getAttribute('content');
-    if (basePath[basePath.length-1] === '/') basePath = basePath.slice(0, basePath.length-1);
-    if (path[0] === '/') path = path.slice(1);
-    return basePath + '/' + path;
+    if (basePath[basePath.length - 1] === '/') basePath = basePath.slice(0, basePath.length - 1);
+    if (targetPath[0] === '/') targetPath = targetPath.slice(1);
+    return `${basePath}/${targetPath}`;
 };
 
-window.importVersioned = function(moduleName) {
+window.importVersioned = function importVersioned(moduleName) {
     const version = document.querySelector('link[href*="/dist/styles.css?version="]').href.split('?version=').pop();
     const importPath = window.baseUrl(`dist/${moduleName}.js?version=${version}`);
     return import(importPath);
 };
 
 // Set events and http services on window
-import events from "./services/events"
-import httpInstance from "./services/http"
 window.$http = httpInstance;
 window.$events = events;
 
 // Translation setup
-// Creates a global function with name 'trans' to be used in the same way as Laravel's translation system
-import Translations from "./services/translations"
+// Creates a global function with name 'trans' to be used in the same way as the Laravel translation system
 const translator = new Translations();
 window.trans = translator.get.bind(translator);
 window.trans_choice = translator.getPlural.bind(translator);
 window.trans_plural = translator.parsePlural.bind(translator);
 
-// Load Components
-import * as components from "./services/components"
-import * as componentMap from "./components";
+// Load & initialise components
 components.register(componentMap);
 window.$components = components;
 components.init();
diff --git a/resources/js/code.mjs b/resources/js/code.mjs
deleted file mode 100644 (file)
index 9adc23b..0000000
+++ /dev/null
@@ -1,346 +0,0 @@
-import CodeMirror from "codemirror";
-import Clipboard from "clipboard/dist/clipboard.min";
-
-// Modes
-import 'codemirror/mode/css/css';
-import 'codemirror/mode/clike/clike';
-import 'codemirror/mode/dart/dart';
-import 'codemirror/mode/diff/diff';
-import 'codemirror/mode/fortran/fortran';
-import 'codemirror/mode/go/go';
-import 'codemirror/mode/haskell/haskell';
-import 'codemirror/mode/htmlmixed/htmlmixed';
-import 'codemirror/mode/javascript/javascript';
-import 'codemirror/mode/julia/julia';
-import 'codemirror/mode/lua/lua';
-import 'codemirror/mode/markdown/markdown';
-import 'codemirror/mode/mllike/mllike';
-import 'codemirror/mode/nginx/nginx';
-import 'codemirror/mode/octave/octave';
-import 'codemirror/mode/perl/perl';
-import 'codemirror/mode/pascal/pascal';
-import 'codemirror/mode/php/php';
-import 'codemirror/mode/powershell/powershell';
-import 'codemirror/mode/properties/properties';
-import 'codemirror/mode/python/python';
-import 'codemirror/mode/ruby/ruby';
-import 'codemirror/mode/rust/rust';
-import 'codemirror/mode/scheme/scheme';
-import 'codemirror/mode/shell/shell';
-import 'codemirror/mode/smarty/smarty';
-import 'codemirror/mode/sql/sql';
-import 'codemirror/mode/stex/stex';
-import 'codemirror/mode/swift/swift';
-import 'codemirror/mode/toml/toml';
-import 'codemirror/mode/twig/twig';
-import 'codemirror/mode/vb/vb';
-import 'codemirror/mode/vbscript/vbscript';
-import 'codemirror/mode/xml/xml';
-import 'codemirror/mode/yaml/yaml';
-
-// Addons
-import 'codemirror/addon/scroll/scrollpastend';
-
-// Mapping of possible languages or formats from user input to their codemirror modes.
-// Value can be a mode string or a function that will receive the code content & return the mode string.
-// The function option is used in the event the exact mode could be dynamic depending on the code.
-const modeMap = {
-    bash: 'shell',
-    css: 'css',
-    c: 'text/x-csrc',
-    java: 'text/x-java',
-    scala: 'text/x-scala',
-    kotlin: 'text/x-kotlin',
-    'c++': 'text/x-c++src',
-    'c#': 'text/x-csharp',
-    csharp: 'text/x-csharp',
-    dart: 'application/dart',
-    diff: 'diff',
-    for: 'fortran',
-    fortran: 'fortran',
-    'f#': 'text/x-fsharp',
-    fsharp: 'text/x-fsharp',
-    go: 'go',
-    haskell: 'haskell',
-    hs: 'haskell',
-    html: 'htmlmixed',
-    ini: 'properties',
-    javascript: 'text/javascript',
-    json: 'application/json',
-    js: 'text/javascript',
-    jl: 'text/x-julia',
-    julia: 'text/x-julia',
-    latex: 'text/x-stex',
-    lua: 'lua',
-    matlab: 'text/x-octave',
-    md: 'markdown',
-    mdown: 'markdown',
-    markdown: 'markdown',
-    ml: 'mllike',
-    mssql: 'text/x-mssql',
-    mysql: 'text/x-mysql',
-    nginx: 'nginx',
-    octave: 'text/x-octave',
-    perl: 'perl',
-    pl: 'perl',
-    powershell: 'powershell',
-    properties: 'properties',
-    ocaml: 'text/x-ocaml',
-    pascal: 'text/x-pascal',
-    pas: 'text/x-pascal',
-    php: (content) => {
-        return content.includes('<?php') ? 'php' : 'text/x-php';
-    },
-    pgsql: 'text/x-pgsql',
-    'pl/sql': 'text/x-plsql',
-    postgresql: 'text/x-pgsql',
-    py: 'python',
-    python: 'python',
-    ruby: 'ruby',
-    rust: 'rust',
-    rb: 'ruby',
-    rs: 'rust',
-    scheme: 'scheme',
-    shell: 'shell',
-    sh: 'shell',
-    smarty: 'smarty',
-    sql: 'text/x-sql',
-    sqlite: 'text/x-sqlite',
-    stext: 'text/x-stex',
-    swift: 'text/x-swift',
-    toml: 'toml',
-    ts: 'text/typescript',
-    twig: 'twig',
-    typescript: 'text/typescript',
-    vbs: 'vbscript',
-    vbscript: 'vbscript',
-    'vb.net': 'text/x-vb',
-    vbnet: 'text/x-vb',
-    xml: 'xml',
-    yaml: 'yaml',
-    yml: 'yaml',
-};
-
-/**
- * Highlight pre elements on a page
- */
-export function highlight() {
-    const codeBlocks = document.querySelectorAll('.page-content pre, .comment-box .content pre');
-    for (const codeBlock of codeBlocks) {
-        highlightElem(codeBlock);
-    }
-}
-
-/**
- * Highlight all code blocks within the given parent element
- * @param {HTMLElement} parent
- */
-export function highlightWithin(parent) {
-    const codeBlocks = parent.querySelectorAll('pre');
-    for (const codeBlock of codeBlocks) {
-        highlightElem(codeBlock);
-    }
-}
-
-/**
- * Add code highlighting to a single element.
- * @param {HTMLElement} elem
- */
-function highlightElem(elem) {
-    const innerCodeElem = elem.querySelector('code[class^=language-]');
-    elem.innerHTML = elem.innerHTML.replace(/<br\s*[\/]?>/gi ,'\n');
-    const content = elem.textContent.trimEnd();
-
-    let mode = '';
-    if (innerCodeElem !== null) {
-        const langName = innerCodeElem.className.replace('language-', '');
-        mode = getMode(langName, content);
-    }
-
-    const cm = CodeMirror(function(elt) {
-        elem.parentNode.replaceChild(elt, elem);
-    }, {
-        value: content,
-        mode:  mode,
-        lineNumbers: true,
-        lineWrapping: false,
-        theme: getTheme(),
-        readOnly: true
-    });
-
-    addCopyIcon(cm);
-}
-
-/**
- * Add a button to a CodeMirror instance which copies the contents to the clipboard upon click.
- * @param cmInstance
- */
-function addCopyIcon(cmInstance) {
-    const copyIcon = `<svg viewBox="0 0 24 24" width="16" height="16" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>`;
-    const copyButton = document.createElement('div');
-    copyButton.classList.add('CodeMirror-copy');
-    copyButton.innerHTML = copyIcon;
-    cmInstance.display.wrapper.appendChild(copyButton);
-
-    const clipboard = new Clipboard(copyButton, {
-        text: function(trigger) {
-            return cmInstance.getValue()
-        }
-    });
-
-    clipboard.on('success', event => {
-        copyButton.classList.add('success');
-        setTimeout(() => {
-            copyButton.classList.remove('success');
-        }, 240);
-    });
-}
-
-/**
- * Search for a codemirror code based off a user suggestion
- * @param {String} suggestion
- * @param {String} content
- * @returns {string}
- */
-function getMode(suggestion, content) {
-    suggestion = suggestion.trim().replace(/^\./g, '').toLowerCase();
-
-    const modeMapType = typeof modeMap[suggestion];
-
-    if (modeMapType === 'undefined') {
-        return '';
-    }
-
-    if (modeMapType === 'function') {
-        return modeMap[suggestion](content);
-    }
-
-    return modeMap[suggestion];
-}
-
-/**
- * Ge the theme to use for CodeMirror instances.
- * @returns {*|string}
- */
-function getTheme() {
-    const darkMode = document.documentElement.classList.contains('dark-mode');
-    return window.codeTheme || (darkMode ? 'darcula' : 'default');
-}
-
-/**
- * Create a CodeMirror instance for showing inside the WYSIWYG editor.
- *  Manages a textarea element to hold code content.
- * @param {HTMLElement} cmContainer
- * @param {String} content
- * @param {String} language
- * @returns {{wrap: Element, editor: *}}
- */
-export function wysiwygView(cmContainer, content, language) {
-    return CodeMirror(cmContainer, {
-        value: content,
-        mode: getMode(language, content),
-        lineNumbers: true,
-        lineWrapping: false,
-        theme: getTheme(),
-        readOnly: true
-    });
-}
-
-
-/**
- * Create a CodeMirror instance to show in the WYSIWYG pop-up editor
- * @param {HTMLElement} elem
- * @param {String} modeSuggestion
- * @returns {*}
- */
-export function popupEditor(elem, modeSuggestion) {
-    const content = elem.textContent;
-
-    return CodeMirror(function(elt) {
-        elem.parentNode.insertBefore(elt, elem);
-        elem.style.display = 'none';
-    }, {
-        value: content,
-        mode:  getMode(modeSuggestion, content),
-        lineNumbers: true,
-        lineWrapping: false,
-        theme: getTheme()
-    });
-}
-
-/**
- * Create an inline editor to replace the given textarea.
- * @param {HTMLTextAreaElement} textArea
- * @param {String} mode
- * @returns {CodeMirror3}
- */
-export function inlineEditor(textArea, mode) {
-    return CodeMirror.fromTextArea(textArea, {
-        mode: getMode(mode, textArea.value),
-        lineNumbers: true,
-        lineWrapping: false,
-        theme: getTheme(),
-    });
-}
-
-/**
- * Set the mode of a codemirror instance.
- * @param cmInstance
- * @param modeSuggestion
- */
-export function setMode(cmInstance, modeSuggestion, content) {
-      cmInstance.setOption('mode', getMode(modeSuggestion, content));
-}
-
-/**
- * Set the content of a cm instance.
- * @param cmInstance
- * @param codeContent
- */
-export function setContent(cmInstance, codeContent) {
-    cmInstance.setValue(codeContent);
-    setTimeout(() => {
-        updateLayout(cmInstance);
-    }, 10);
-}
-
-/**
- * Update the layout (codemirror refresh) of a cm instance.
- * @param cmInstance
- */
-export function updateLayout(cmInstance) {
-    cmInstance.refresh();
-}
-
-/**
- * Get a CodeMirror instance to use for the markdown editor.
- * @param {HTMLElement} elem
- * @returns {*}
- */
-export function markdownEditor(elem) {
-    const content = elem.textContent;
-    const config = {
-        value: content,
-        mode: "markdown",
-        lineNumbers: true,
-        lineWrapping: true,
-        theme: getTheme(),
-        scrollPastEnd: true,
-    };
-
-    window.$events.emitPublic(elem, 'editor-markdown-cm::pre-init', {config});
-
-    return CodeMirror(function (elt) {
-        elem.parentNode.insertBefore(elt, elem);
-        elem.style.display = 'none';
-    }, config);
-}
-
-/**
- * Get the 'meta' key dependent on the user's system.
- * @returns {string}
- */
-export function getMetaKey() {
-    let mac = CodeMirror.keyMap["default"] == CodeMirror.keyMap.macDefault;
-    return mac ? "Cmd" : "Ctrl";
-}
\ No newline at end of file
diff --git a/resources/js/code/index.mjs b/resources/js/code/index.mjs
new file mode 100644 (file)
index 0000000..e51472d
--- /dev/null
@@ -0,0 +1,206 @@
+import {EditorView, keymap} from '@codemirror/view';
+
+import {copyTextToClipboard} from '../services/clipboard';
+import {viewerExtensions, editorExtensions} from './setups';
+import {createView} from './views';
+import {SimpleEditorInterface} from './simple-editor-interface';
+
+/**
+ * Add a button to a CodeMirror instance which copies the contents to the clipboard upon click.
+ * @param {EditorView} editorView
+ */
+function addCopyIcon(editorView) {
+    const copyIcon = '<svg viewBox="0 0 24 24" width="16" height="16" xmlns="http://www.w3.org/2000/svg"><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>';
+    const checkIcon = '<svg viewBox="0 0 24 24" width="16" height="16" xmlns="http://www.w3.org/2000/svg"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>';
+    const copyButton = document.createElement('button');
+    copyButton.setAttribute('type', 'button');
+    copyButton.classList.add('cm-copy-button');
+    copyButton.innerHTML = copyIcon;
+    editorView.dom.appendChild(copyButton);
+
+    const notifyTime = 620;
+    const transitionTime = 60;
+    copyButton.addEventListener('click', () => {
+        copyTextToClipboard(editorView.state.doc.toString());
+        copyButton.classList.add('success');
+
+        setTimeout(() => {
+            copyButton.innerHTML = checkIcon;
+        }, transitionTime / 2);
+
+        setTimeout(() => {
+            copyButton.classList.remove('success');
+        }, notifyTime);
+
+        setTimeout(() => {
+            copyButton.innerHTML = copyIcon;
+        }, notifyTime + (transitionTime / 2));
+    });
+}
+
+/**
+ * Add code highlighting to a single element.
+ * @param {HTMLElement} elem
+ */
+function highlightElem(elem) {
+    const innerCodeElem = elem.querySelector('code[class^=language-]');
+    elem.innerHTML = elem.innerHTML.replace(/<br\s*\/?>/gi, '\n');
+    const content = elem.textContent.trimEnd();
+
+    let langName = '';
+    if (innerCodeElem !== null) {
+        langName = innerCodeElem.className.replace('language-', '');
+    }
+
+    const wrapper = document.createElement('div');
+    elem.parentNode.insertBefore(wrapper, elem);
+
+    const ev = createView({
+        parent: wrapper,
+        doc: content,
+        extensions: viewerExtensions(wrapper),
+    });
+
+    const editor = new SimpleEditorInterface(ev);
+    editor.setMode(langName, content);
+
+    elem.remove();
+    addCopyIcon(ev);
+}
+
+/**
+ * Highlight all code blocks within the given parent element
+ * @param {HTMLElement} parent
+ */
+export function highlightWithin(parent) {
+    const codeBlocks = parent.querySelectorAll('pre');
+    for (const codeBlock of codeBlocks) {
+        highlightElem(codeBlock);
+    }
+}
+
+/**
+ * Highlight pre elements on a page
+ */
+export function highlight() {
+    const codeBlocks = document.querySelectorAll('.page-content pre, .comment-box .content pre');
+    for (const codeBlock of codeBlocks) {
+        highlightElem(codeBlock);
+    }
+}
+
+/**
+ * Create a CodeMirror instance for showing inside the WYSIWYG editor.
+ * Manages a textarea element to hold code content.
+ * @param {HTMLElement} cmContainer
+ * @param {ShadowRoot} shadowRoot
+ * @param {String} content
+ * @param {String} language
+ * @returns {SimpleEditorInterface}
+ */
+export function wysiwygView(cmContainer, shadowRoot, content, language) {
+    const ev = createView({
+        parent: cmContainer,
+        doc: content,
+        extensions: viewerExtensions(cmContainer),
+        root: shadowRoot,
+    });
+
+    const editor = new SimpleEditorInterface(ev);
+    editor.setMode(language, content);
+
+    return editor;
+}
+
+/**
+ * Create a CodeMirror instance to show in the WYSIWYG pop-up editor
+ * @param {HTMLElement} elem
+ * @param {String} modeSuggestion
+ * @returns {SimpleEditorInterface}
+ */
+export function popupEditor(elem, modeSuggestion) {
+    const content = elem.textContent;
+    const config = {
+        parent: elem.parentElement,
+        doc: content,
+        extensions: [
+            ...editorExtensions(elem.parentElement),
+            EditorView.updateListener.of(v => {
+                if (v.docChanged) {
+                    // textArea.value = v.state.doc.toString();
+                }
+            }),
+        ],
+    };
+
+    // Create editor, hide original input
+    const editor = new SimpleEditorInterface(createView(config));
+    editor.setMode(modeSuggestion, content);
+    elem.style.display = 'none';
+
+    return editor;
+}
+
+/**
+ * Create an inline editor to replace the given textarea.
+ * @param {HTMLTextAreaElement} textArea
+ * @param {String} mode
+ * @returns {SimpleEditorInterface}
+ */
+export function inlineEditor(textArea, mode) {
+    const content = textArea.value;
+    const config = {
+        parent: textArea.parentElement,
+        doc: content,
+        extensions: [
+            ...editorExtensions(textArea.parentElement),
+            EditorView.updateListener.of(v => {
+                if (v.docChanged) {
+                    textArea.value = v.state.doc.toString();
+                }
+            }),
+        ],
+    };
+
+    // Create editor view, hide original input
+    const ev = createView(config);
+    const editor = new SimpleEditorInterface(ev);
+    editor.setMode(mode, content);
+    textArea.style.display = 'none';
+
+    return editor;
+}
+
+/**
+ * Get a CodeMirror instance to use for the markdown editor.
+ * @param {HTMLElement} elem
+ * @param {function} onChange
+ * @param {object} domEventHandlers
+ * @param {Array} keyBindings
+ * @returns {EditorView}
+ */
+export function markdownEditor(elem, onChange, domEventHandlers, keyBindings) {
+    const content = elem.textContent;
+    const config = {
+        parent: elem.parentElement,
+        doc: content,
+        extensions: [
+            keymap.of(keyBindings),
+            ...editorExtensions(elem.parentElement),
+            EditorView.updateListener.of(v => {
+                onChange(v);
+            }),
+            EditorView.domEventHandlers(domEventHandlers),
+        ],
+    };
+
+    // Emit a pre-event public event to allow tweaking of the configure before view creation.
+    window.$events.emitPublic(elem, 'editor-markdown-cm6::pre-init', {editorViewConfig: config});
+
+    // Create editor view, hide original input
+    const ev = createView(config);
+    (new SimpleEditorInterface(ev)).setMode('markdown', '');
+    elem.style.display = 'none';
+
+    return ev;
+}
diff --git a/resources/js/code/languages.js b/resources/js/code/languages.js
new file mode 100644 (file)
index 0000000..c0f9e6d
--- /dev/null
@@ -0,0 +1,117 @@
+import {StreamLanguage} from '@codemirror/language';
+
+import {css} from '@codemirror/lang-css';
+import {json} from '@codemirror/lang-json';
+import {javascript} from '@codemirror/lang-javascript';
+import {html} from '@codemirror/lang-html';
+import {markdown} from '@codemirror/lang-markdown';
+import {php} from '@codemirror/lang-php';
+import {twig} from '@ssddanbrown/codemirror-lang-twig';
+import {xml} from '@codemirror/lang-xml';
+
+const legacyLoad = async mode => {
+    const modes = await window.importVersioned('legacy-modes');
+    return StreamLanguage.define(modes[mode]);
+};
+
+// Mapping of possible languages or formats from user input to their codemirror modes.
+// Value can be a mode string or a function that will receive the code content & return the mode string.
+// The function option is used in the event the exact mode could be dynamic depending on the code.
+const modeMap = {
+    bash: () => legacyLoad('shell'),
+    c: () => legacyLoad('c'),
+    css: async () => css(),
+    'c++': () => legacyLoad('cpp'),
+    'c#': () => legacyLoad('csharp'),
+    clj: () => legacyLoad('clojure'),
+    clojure: () => legacyLoad('clojure'),
+    csharp: () => legacyLoad('csharp'),
+    dart: () => legacyLoad('dart'),
+    diff: () => legacyLoad('diff'),
+    for: () => legacyLoad('fortran'),
+    fortran: () => legacyLoad('fortran'),
+    'f#': () => legacyLoad('fSharp'),
+    fsharp: () => legacyLoad('fSharp'),
+    go: () => legacyLoad('go'),
+    haskell: () => legacyLoad('haskell'),
+    hs: () => legacyLoad('haskell'),
+    html: async () => html(),
+    ini: () => legacyLoad('properties'),
+    java: () => legacyLoad('java'),
+    javascript: async () => javascript(),
+    json: async () => json(),
+    js: async () => javascript(),
+    jl: () => legacyLoad('julia'),
+    julia: () => legacyLoad('julia'),
+    kotlin: () => legacyLoad('kotlin'),
+    latex: () => legacyLoad('stex'),
+    lua: () => legacyLoad('lua'),
+    markdown: async () => markdown(),
+    matlab: () => legacyLoad('octave'),
+    md: async () => markdown(),
+    mdown: async () => markdown(),
+    ml: () => legacyLoad('sml'),
+    mssql: () => legacyLoad('msSQL'),
+    mysql: () => legacyLoad('mySQL'),
+    nginx: () => legacyLoad('nginx'),
+    octave: () => legacyLoad('octave'),
+    pas: () => legacyLoad('pascal'),
+    pascal: () => legacyLoad('pascal'),
+    perl: () => legacyLoad('perl'),
+    pgsql: () => legacyLoad('pgSQL'),
+    php: async code => {
+        const hasTags = code.includes('<?php');
+        return php({plain: !hasTags});
+    },
+    pl: () => legacyLoad('perl'),
+    'pl/sql': () => legacyLoad('plSQL'),
+    postgresql: () => legacyLoad('pgSQL'),
+    powershell: () => legacyLoad('powerShell'),
+    properties: () => legacyLoad('properties'),
+    ocaml: () => legacyLoad('oCaml'),
+    py: () => legacyLoad('python'),
+    python: () => legacyLoad('python'),
+    rb: () => legacyLoad('ruby'),
+    rs: () => legacyLoad('rust'),
+    ruby: () => legacyLoad('ruby'),
+    rust: () => legacyLoad('rust'),
+    scala: () => legacyLoad('scala'),
+    scheme: () => legacyLoad('scheme'),
+    shell: () => legacyLoad('shell'),
+    sh: () => legacyLoad('shell'),
+    smarty: () => legacyLoad('smarty'),
+    stext: () => legacyLoad('stex'),
+    swift: () => legacyLoad('swift'),
+    toml: () => legacyLoad('toml'),
+    ts: async () => javascript({typescript: true}),
+    twig: async () => twig(),
+    typescript: async () => javascript({typescript: true}),
+    sql: () => legacyLoad('standardSQL'),
+    sqlite: () => legacyLoad('sqlite'),
+    vbs: () => legacyLoad('vbScript'),
+    vbscript: () => legacyLoad('vbScript'),
+    'vb.net': () => legacyLoad('vb'),
+    vbnet: () => legacyLoad('vb'),
+    xml: async () => xml(),
+    yaml: () => legacyLoad('yaml'),
+    yml: () => legacyLoad('yaml'),
+};
+
+/**
+ * Get the relevant codemirror language extension based upon the given language
+ * suggestion and content.
+ * @param {String} langSuggestion
+ * @param {String} content
+ * @returns {Promise<StreamLanguage|LanguageSupport>}
+ */
+export function getLanguageExtension(langSuggestion, content) {
+    const suggestion = langSuggestion.trim().replace(/^\./g, '').toLowerCase();
+
+    const language = modeMap[suggestion];
+
+    if (typeof language === 'undefined') {
+        return undefined;
+    }
+
+    return language(content);
+}
diff --git a/resources/js/code/legacy-modes.mjs b/resources/js/code/legacy-modes.mjs
new file mode 100644 (file)
index 0000000..55a804b
--- /dev/null
@@ -0,0 +1,32 @@
+export {
+    c, cpp, csharp, java, kotlin, scala, dart,
+} from '@codemirror/legacy-modes/mode/clike';
+export {clojure} from '@codemirror/legacy-modes/mode/clojure';
+export {diff} from '@codemirror/legacy-modes/mode/diff';
+export {fortran} from '@codemirror/legacy-modes/mode/fortran';
+export {go} from '@codemirror/legacy-modes/mode/go';
+export {haskell} from '@codemirror/legacy-modes/mode/haskell';
+export {julia} from '@codemirror/legacy-modes/mode/julia';
+export {lua} from '@codemirror/legacy-modes/mode/lua';
+export {oCaml, fSharp, sml} from '@codemirror/legacy-modes/mode/mllike';
+export {nginx} from '@codemirror/legacy-modes/mode/nginx';
+export {octave} from '@codemirror/legacy-modes/mode/octave';
+export {perl} from '@codemirror/legacy-modes/mode/perl';
+export {pascal} from '@codemirror/legacy-modes/mode/pascal';
+export {powerShell} from '@codemirror/legacy-modes/mode/powershell';
+export {properties} from '@codemirror/legacy-modes/mode/properties';
+export {python} from '@codemirror/legacy-modes/mode/python';
+export {ruby} from '@codemirror/legacy-modes/mode/ruby';
+export {rust} from '@codemirror/legacy-modes/mode/rust';
+export {scheme} from '@codemirror/legacy-modes/mode/scheme';
+export {shell} from '@codemirror/legacy-modes/mode/shell';
+export {
+    standardSQL, pgSQL, msSQL, mySQL, sqlite, plSQL,
+} from '@codemirror/legacy-modes/mode/sql';
+export {stex} from '@codemirror/legacy-modes/mode/stex';
+export {swift} from '@codemirror/legacy-modes/mode/swift';
+export {toml} from '@codemirror/legacy-modes/mode/toml';
+export {vb} from '@codemirror/legacy-modes/mode/vb';
+export {vbScript} from '@codemirror/legacy-modes/mode/vbscript';
+export {yaml} from '@codemirror/legacy-modes/mode/yaml';
+export {smarty} from '@ssddanbrown/codemirror-lang-smarty';
diff --git a/resources/js/code/setups.js b/resources/js/code/setups.js
new file mode 100644 (file)
index 0000000..52b9cc1
--- /dev/null
@@ -0,0 +1,58 @@
+import {
+    EditorView, keymap, drawSelection, highlightActiveLine, dropCursor,
+    rectangularSelection, lineNumbers, highlightActiveLineGutter,
+} from '@codemirror/view';
+import {bracketMatching} from '@codemirror/language';
+import {
+    defaultKeymap, history, historyKeymap, indentWithTab,
+} from '@codemirror/commands';
+import {EditorState} from '@codemirror/state';
+import {getTheme} from './themes';
+
+/**
+ * @param {Element} parentEl
+ * @return {(Extension[]|{extension: Extension}|readonly Extension[])[]}
+ */
+function common(parentEl) {
+    return [
+        getTheme(parentEl),
+        lineNumbers(),
+        highlightActiveLineGutter(),
+        drawSelection(),
+        dropCursor(),
+        bracketMatching(),
+        rectangularSelection(),
+        highlightActiveLine(),
+    ];
+}
+
+/**
+ * @param {Element} parentEl
+ * @return {*[]}
+ */
+export function viewerExtensions(parentEl) {
+    return [
+        ...common(parentEl),
+        keymap.of([
+            ...defaultKeymap,
+        ]),
+        EditorState.readOnly.of(true),
+    ];
+}
+
+/**
+ * @param {Element} parentEl
+ * @return {*[]}
+ */
+export function editorExtensions(parentEl) {
+    return [
+        ...common(parentEl),
+        history(),
+        keymap.of([
+            ...defaultKeymap,
+            ...historyKeymap,
+            indentWithTab,
+        ]),
+        EditorView.lineWrapping,
+    ];
+}
diff --git a/resources/js/code/simple-editor-interface.js b/resources/js/code/simple-editor-interface.js
new file mode 100644 (file)
index 0000000..63456cd
--- /dev/null
@@ -0,0 +1,47 @@
+import {updateViewLanguage} from './views';
+
+export class SimpleEditorInterface {
+
+    /**
+     * @param {EditorView} editorView
+     */
+    constructor(editorView) {
+        this.ev = editorView;
+    }
+
+    /**
+     * Get the contents of an editor instance.
+     * @return {string}
+     */
+    getContent() {
+        return this.ev.state.doc.toString();
+    }
+
+    /**
+     * Set the contents of an editor instance.
+     * @param content
+     */
+    setContent(content) {
+        const {doc} = this.ev.state;
+        this.ev.dispatch({
+            changes: {from: 0, to: doc.length, insert: content},
+        });
+    }
+
+    /**
+     * Return focus to the editor instance.
+     */
+    focus() {
+        this.ev.focus();
+    }
+
+    /**
+     * Set the language mode of the editor instance.
+     * @param {String} mode
+     * @param {String} content
+     */
+    setMode(mode, content = '') {
+        updateViewLanguage(this.ev, mode, content);
+    }
+
+}
diff --git a/resources/js/code/themes.js b/resources/js/code/themes.js
new file mode 100644 (file)
index 0000000..b3635bd
--- /dev/null
@@ -0,0 +1,131 @@
+import {tags} from '@lezer/highlight';
+import {HighlightStyle, syntaxHighlighting} from '@codemirror/language';
+import {EditorView} from '@codemirror/view';
+import {oneDarkHighlightStyle, oneDarkTheme} from '@codemirror/theme-one-dark';
+
+const defaultLightHighlightStyle = HighlightStyle.define([
+    {
+        tag: tags.meta,
+        color: '#388938',
+    },
+    {
+        tag: tags.link,
+        textDecoration: 'underline',
+    },
+    {
+        tag: tags.heading,
+        textDecoration: 'underline',
+        fontWeight: 'bold',
+    },
+    {
+        tag: tags.emphasis,
+        fontStyle: 'italic',
+    },
+    {
+        tag: tags.strong,
+        fontWeight: 'bold',
+    },
+    {
+        tag: tags.strikethrough,
+        textDecoration: 'line-through',
+    },
+    {
+        tag: tags.keyword,
+        color: '#708',
+    },
+    {
+        tag: [tags.atom, tags.bool, tags.url, tags.contentSeparator, tags.labelName],
+        color: '#219',
+    },
+    {
+        tag: [tags.literal, tags.inserted],
+        color: '#164',
+    },
+    {
+        tag: [tags.string, tags.deleted],
+        color: '#a11',
+    },
+    {
+        tag: [tags.regexp, tags.escape, tags.special(tags.string)],
+        color: '#e40',
+    },
+    {
+        tag: tags.definition(tags.variableName),
+        color: '#00f',
+    },
+    {
+        tag: tags.local(tags.variableName),
+        color: '#30a',
+    },
+    {
+        tag: [tags.typeName, tags.namespace],
+        color: '#085',
+    },
+    {
+        tag: tags.className,
+        color: '#167',
+    },
+    {
+        tag: [tags.special(tags.variableName), tags.macroName],
+        color: '#256',
+    },
+    {
+        tag: tags.definition(tags.propertyName),
+        color: '#00c',
+    },
+    {
+        tag: tags.compareOperator,
+        color: '#708',
+    },
+    {
+        tag: tags.comment,
+        color: '#940',
+    },
+    {
+        tag: tags.invalid,
+        color: '#f00',
+    },
+]);
+
+const defaultThemeSpec = {
+    '&': {
+        backgroundColor: '#FFF',
+        color: '#000',
+    },
+    '&.cm-focused': {
+        outline: 'none',
+    },
+    '.cm-line': {
+        lineHeight: '1.6',
+    },
+};
+
+/**
+ * Get the theme extension to use for editor view instance.
+ * @returns {Extension[]}
+ */
+export function getTheme(viewParentEl) {
+    const darkMode = document.documentElement.classList.contains('dark-mode');
+    let viewTheme = darkMode ? oneDarkTheme : EditorView.theme(defaultThemeSpec);
+    let highlightStyle = darkMode ? oneDarkHighlightStyle : defaultLightHighlightStyle;
+
+    const eventData = {
+        darkModeActive: darkMode,
+        registerViewTheme(builder) {
+            const spec = builder();
+            if (spec) {
+                viewTheme = EditorView.theme(spec);
+            }
+        },
+        registerHighlightStyle(builder) {
+            const tagStyles = builder(tags) || [];
+            if (tagStyles.length) {
+                highlightStyle = HighlightStyle.define(tagStyles);
+            }
+        },
+    };
+
+    window.$events.emitPublic(viewParentEl, 'library-cm6::configure-theme', eventData);
+
+    return [viewTheme, syntaxHighlighting(highlightStyle)];
+}
diff --git a/resources/js/code/views.js b/resources/js/code/views.js
new file mode 100644 (file)
index 0000000..12148ca
--- /dev/null
@@ -0,0 +1,38 @@
+import {Compartment} from '@codemirror/state';
+import {EditorView} from '@codemirror/view';
+import {getLanguageExtension} from './languages';
+
+const viewLangCompartments = new WeakMap();
+
+/**
+ * Create a new editor view.
+ *
+ * @param {{parent: Element, doc: String, extensions: Array}} config
+ * @returns {EditorView}
+ */
+export function createView(config) {
+    const langCompartment = new Compartment();
+    config.extensions.push(langCompartment.of([]));
+
+    const ev = new EditorView(config);
+
+    viewLangCompartments.set(ev, langCompartment);
+
+    return ev;
+}
+
+/**
+ * Set the language mode of an EditorView.
+ *
+ * @param {EditorView} ev
+ * @param {string} modeSuggestion
+ * @param {string} content
+ */
+export async function updateViewLanguage(ev, modeSuggestion, content) {
+    const compartment = viewLangCompartments.get(ev);
+    const language = await getLanguageExtension(modeSuggestion, content);
+
+    ev.dispatch({
+        effects: compartment.reconfigure(language || []),
+    });
+}
index 19d2249fb28ece1607f00367e7e443b75ea8f8d5..3213c4835aa45fd5bad209e34b4b5a9e6b44967c 100644 (file)
@@ -1,6 +1,6 @@
-import {onChildEvent} from "../services/dom";
-import {uniqueId} from "../services/util";
-import {Component} from "./component";
+import {onChildEvent} from '../services/dom';
+import {uniqueId} from '../services/util';
+import {Component} from './component';
 
 /**
  * AddRemoveRows
@@ -8,6 +8,7 @@ import {Component} from "./component";
  * Needs a model row to use when adding a new row.
  */
 export class AddRemoveRows extends Component {
+
     setup() {
         this.modelRow = this.$refs.model;
         this.addButton = this.$refs.add;
@@ -19,7 +20,7 @@ export class AddRemoveRows extends Component {
     setupListeners() {
         this.addButton.addEventListener('click', this.add.bind(this));
 
-        onChildEvent(this.$el, this.removeSelector, 'click', (e) => {
+        onChildEvent(this.$el, this.removeSelector, 'click', e => {
             const row = e.target.closest(this.rowSelector);
             row.remove();
         });
@@ -44,9 +45,10 @@ export class AddRemoveRows extends Component {
      */
     setClonedInputNames(clone) {
         const rowId = uniqueId();
-        const randRowIdElems = clone.querySelectorAll(`[name*="randrowid"]`);
+        const randRowIdElems = clone.querySelectorAll('[name*="randrowid"]');
         for (const elem of randRowIdElems) {
             elem.name = elem.name.split('randrowid').join(rowId);
         }
     }
-}
\ No newline at end of file
+
+}
index f1af7f6cb10e97d2e65cfb1c4e49d1d5a4e7580e..aa2801f19e666c52ee9f1d738b71478ec105552a 100644 (file)
@@ -1,7 +1,8 @@
-import {onSelect} from "../services/dom";
-import {Component} from "./component";
+import {onSelect} from '../services/dom';
+import {Component} from './component';
 
 export class AjaxDeleteRow extends Component {
+
     setup() {
         this.row = this.$el;
         this.url = this.$opts.url;
@@ -19,9 +20,10 @@ export class AjaxDeleteRow extends Component {
                 window.$events.emit('success', resp.data.message);
             }
             this.row.remove();
-        }).catch(err => {
+        }).catch(() => {
             this.row.style.opacity = null;
             this.row.style.pointerEvents = null;
         });
     }
-}
\ No newline at end of file
+
+}
index 6f4e5af08c8bfbe20483ea0805cf4a44261c3861..583dde5724424defb44906504ab4973a2fb88af5 100644 (file)
@@ -1,5 +1,5 @@
-import {onEnterPress, onSelect} from "../services/dom";
-import {Component} from "./component";
+import {onEnterPress, onSelect} from '../services/dom';
+import {Component} from './component';
 
 /**
  * Ajax Form
@@ -11,6 +11,7 @@ import {Component} from "./component";
  * otherwise will act as a fake form element.
  */
 export class AjaxForm extends Component {
+
     setup() {
         this.container = this.$el;
         this.responseContainer = this.container;
@@ -27,7 +28,6 @@ export class AjaxForm extends Component {
     }
 
     setupListeners() {
-
         if (this.container.tagName === 'FORM') {
             this.container.addEventListener('submit', this.submitRealForm.bind(this));
             return;
@@ -43,7 +43,7 @@ export class AjaxForm extends Component {
 
     submitFakeForm() {
         const fd = new FormData();
-        const inputs = this.container.querySelectorAll(`[name]`);
+        const inputs = this.container.querySelectorAll('[name]');
         for (const input of inputs) {
             fd.append(input.getAttribute('name'), input.value);
         }
@@ -76,4 +76,4 @@ export class AjaxForm extends Component {
         this.responseContainer.style.pointerEvents = null;
     }
 
-}
\ No newline at end of file
+}
index dfefd9b7f84afb24c1cdb77dd8dbe22281dc1533..4db09977fec7ff0d20bf10192b4b59ca3caad1aa 100644 (file)
@@ -1,4 +1,4 @@
-import {Component} from "./component";
+import {Component} from './component';
 
 /**
  * Attachments List
@@ -13,11 +13,11 @@ export class AttachmentsList extends Component {
     }
 
     setupListeners() {
-        const isExpectedKey = (event) => event.key === 'Control' || event.key === 'Meta';
+        const isExpectedKey = event => event.key === 'Control' || event.key === 'Meta';
         window.addEventListener('keydown', event => {
-             if (isExpectedKey(event)) {
+            if (isExpectedKey(event)) {
                 this.addOpenQueryToLinks();
-             }
+            }
         }, {passive: true});
         window.addEventListener('keyup', event => {
             if (isExpectedKey(event)) {
@@ -30,7 +30,7 @@ export class AttachmentsList extends Component {
         const links = this.container.querySelectorAll('a.attachment-file');
         for (const link of links) {
             if (link.href.split('?')[1] !== 'open=true') {
-                link.href = link.href + '?open=true';
+                link.href += '?open=true';
                 link.setAttribute('target', '_blank');
             }
         }
@@ -43,4 +43,5 @@ export class AttachmentsList extends Component {
             link.removeAttribute('target');
         }
     }
-}
\ No newline at end of file
+
+}
index d8a506270dfcb3c8764364db961d921a9300c24a..9555a59e811c999aa10a98fa4e4df3337f3595f6 100644 (file)
@@ -1,5 +1,5 @@
-import {showLoading} from "../services/dom";
-import {Component} from "./component";
+import {showLoading} from '../services/dom';
+import {Component} from './component';
 
 export class Attachments extends Component {
 
@@ -27,7 +27,7 @@ export class Attachments extends Component {
             this.startEdit(event.detail.id);
         });
 
-        this.container.addEventListener('event-emit-select-edit-back', event => {
+        this.container.addEventListener('event-emit-select-edit-back', () => {
             this.stopEdit();
         });
 
@@ -73,4 +73,4 @@ export class Attachments extends Component {
         this.listContainer.classList.remove('hidden');
     }
 
-}
\ No newline at end of file
+}
index c8726ca7e2914d5099b38f538664da7f11c44592..c78ef55492a9ae60138b65f809875f57cadc2a05 100644 (file)
@@ -1,4 +1,4 @@
-import {Component} from "./component";
+import {Component} from './component';
 
 export class AutoSubmit extends Component {
 
@@ -8,4 +8,4 @@ export class AutoSubmit extends Component {
         this.form.submit();
     }
 
-}
\ No newline at end of file
+}
index b4e6c5957879b759c7e9a358fcd5b528c7673b22..92a6c6af3f43fde8ff5288c460f92c76eab31650 100644 (file)
@@ -1,6 +1,7 @@
-import {escapeHtml} from "../services/util";
-import {onChildEvent} from "../services/dom";
-import {Component} from "./component";
+import {escapeHtml} from '../services/util';
+import {onChildEvent} from '../services/dom';
+import {Component} from './component';
+import {KeyboardNavigationHandler} from '../services/keyboard-navigation';
 
 const ajaxCache = {};
 
@@ -8,6 +9,7 @@ const ajaxCache = {};
  * AutoSuggest
  */
 export class AutoSuggest extends Component {
+
     setup() {
         this.parent = this.$el.parentElement;
         this.container = this.$el;
@@ -21,26 +23,31 @@ export class AutoSuggest extends Component {
     }
 
     setupListeners() {
+        const navHandler = new KeyboardNavigationHandler(
+            this.list,
+            () => {
+                this.input.focus();
+                setTimeout(() => this.hideSuggestions(), 1);
+            },
+            event => {
+                event.preventDefault();
+                this.selectSuggestion(event.target.textContent);
+            },
+        );
+        navHandler.shareHandlingToEl(this.input);
+
+        onChildEvent(this.list, '.text-item', 'click', (event, el) => {
+            this.selectSuggestion(el.textContent);
+        });
+
         this.input.addEventListener('input', this.requestSuggestions.bind(this));
         this.input.addEventListener('focus', this.requestSuggestions.bind(this));
+        this.input.addEventListener('blur', this.hideSuggestionsIfFocusedLost.bind(this));
         this.input.addEventListener('keydown', event => {
             if (event.key === 'Tab') {
                 this.hideSuggestions();
             }
         });
-
-        this.input.addEventListener('blur', this.hideSuggestionsIfFocusedLost.bind(this));
-        this.container.addEventListener('keydown', this.containerKeyDown.bind(this));
-
-        onChildEvent(this.list, 'button', 'click', (event, el) => {
-            this.selectSuggestion(el.textContent);
-        });
-        onChildEvent(this.list, 'button', 'keydown', (event, el) => {
-            if (event.key === 'Enter') {
-                this.selectSuggestion(el.textContent);
-            }
-        });
-
     }
 
     selectSuggestion(value) {
@@ -52,36 +59,6 @@ export class AutoSuggest extends Component {
         this.hideSuggestions();
     }
 
-    containerKeyDown(event) {
-        if (event.key === 'Enter') event.preventDefault();
-        if (this.list.classList.contains('hidden')) return;
-
-        // Down arrow
-        if (event.key === 'ArrowDown') {
-            this.moveFocus(true);
-            event.preventDefault();
-        }
-        // Up Arrow
-        else if (event.key === 'ArrowUp') {
-            this.moveFocus(false);
-            event.preventDefault();
-        }
-        // Escape key
-        else if (event.key === 'Escape') {
-            this.hideSuggestions();
-            event.preventDefault();
-        }
-    }
-
-    moveFocus(forward = true) {
-        const focusables = Array.from(this.container.querySelectorAll('input,button'));
-        const index = focusables.indexOf(document.activeElement);
-        const newFocus = focusables[index + (forward ? 1 : -1)];
-        if (newFocus) {
-            newFocus.focus()
-        }
-    }
-
     async requestSuggestions() {
         if (Date.now() - this.lastPopulated < 50) {
             return;
@@ -91,9 +68,7 @@ export class AutoSuggest extends Component {
         const search = this.input.value.toLowerCase();
         const suggestions = await this.loadSuggestions(search, nameFilter);
 
-        const toShow = suggestions.filter(val => {
-            return search === '' || val.toLowerCase().startsWith(search);
-        }).slice(0, 10);
+        const toShow = suggestions.filter(val => search === '' || val.toLowerCase().startsWith(search)).slice(0, 10);
 
         this.displaySuggestions(toShow);
     }
@@ -129,12 +104,15 @@ export class AutoSuggest extends Component {
      */
     displaySuggestions(suggestions) {
         if (suggestions.length === 0) {
-            return this.hideSuggestions();
+            this.hideSuggestions();
+            return;
         }
 
-        this.list.innerHTML = suggestions.map(value => `<li><button type="button" class="text-item">${escapeHtml(value)}</button></li>`).join('');
+        // This used to use <button>s but was changed to div elements since Safari would not focus on buttons
+        // on which causes a range of other complexities related to focus handling.
+        this.list.innerHTML = suggestions.map(value => `<li><div tabindex="0" class="text-item">${escapeHtml(value)}</div></li>`).join('');
         this.list.style.display = 'block';
-        for (const button of this.list.querySelectorAll('button')) {
+        for (const button of this.list.querySelectorAll('.text-item')) {
             button.addEventListener('blur', this.hideSuggestionsIfFocusedLost.bind(this));
         }
     }
@@ -148,4 +126,5 @@ export class AutoSuggest extends Component {
             this.hideSuggestions();
         }
     }
-}
\ No newline at end of file
+
+}
index 4f0a46f009b19822332bd680f766bc205acc43b9..046e640d10a06175d9bcb064574d038785991282 100644 (file)
@@ -1,4 +1,4 @@
-import {Component} from "./component";
+import {Component} from './component';
 
 export class BackToTop extends Component {
 
@@ -18,7 +18,7 @@ export class BackToTop extends Component {
     }
 
     onPageScroll() {
-        let scrollTopPos = document.documentElement.scrollTop || document.body.scrollTop || 0;
+        const scrollTopPos = document.documentElement.scrollTop || document.body.scrollTop || 0;
         if (!this.showing && scrollTopPos > this.breakPoint) {
             this.button.style.display = 'block';
             this.showing = true;
@@ -35,15 +35,15 @@ export class BackToTop extends Component {
     }
 
     scrollToTop() {
-        let targetTop = this.targetElem.getBoundingClientRect().top;
-        let scrollElem = document.documentElement.scrollTop ? document.documentElement : document.body;
-        let duration = 300;
-        let start = Date.now();
-        let scrollStart = this.targetElem.getBoundingClientRect().top;
+        const targetTop = this.targetElem.getBoundingClientRect().top;
+        const scrollElem = document.documentElement.scrollTop ? document.documentElement : document.body;
+        const duration = 300;
+        const start = Date.now();
+        const scrollStart = this.targetElem.getBoundingClientRect().top;
 
         function setPos() {
-            let percentComplete = (1-((Date.now() - start) / duration));
-            let target = Math.abs(percentComplete * scrollStart);
+            const percentComplete = (1 - ((Date.now() - start) / duration));
+            const target = Math.abs(percentComplete * scrollStart);
             if (percentComplete > 0) {
                 scrollElem.scrollTop = target;
                 requestAnimationFrame(setPos.bind(this));
@@ -55,4 +55,4 @@ export class BackToTop extends Component {
         requestAnimationFrame(setPos.bind(this));
     }
 
-}
\ No newline at end of file
+}
index 5ae283fd017520b02adbb10fc0c56980eda2bd8f..2ba7d5d36b00dc912be8c3d1f25f1b5e464d2ffe 100644 (file)
@@ -1,25 +1,25 @@
-import Sortable, {MultiDrag} from "sortablejs";
-import {Component} from "./component";
-import {htmlToDom} from "../services/dom";
+import Sortable, {MultiDrag} from 'sortablejs';
+import {Component} from './component';
+import {htmlToDom} from '../services/dom';
 
 // Auto sort control
 const sortOperations = {
-    name: function(a, b) {
+    name(a, b) {
         const aName = a.getAttribute('data-name').trim().toLowerCase();
         const bName = b.getAttribute('data-name').trim().toLowerCase();
         return aName.localeCompare(bName);
     },
-    created: function(a, b) {
+    created(a, b) {
         const aTime = Number(a.getAttribute('data-created'));
         const bTime = Number(b.getAttribute('data-created'));
         return bTime - aTime;
     },
-    updated: function(a, b) {
+    updated(a, b) {
         const aTime = Number(a.getAttribute('data-updated'));
         const bTime = Number(b.getAttribute('data-updated'));
         return bTime - aTime;
     },
-    chaptersFirst: function(a, b) {
+    chaptersFirst(a, b) {
         const aType = a.getAttribute('data-type');
         const bType = b.getAttribute('data-type');
         if (aType === bType) {
@@ -27,7 +27,7 @@ const sortOperations = {
         }
         return (aType === 'chapter' ? -1 : 1);
     },
-    chaptersLast: function(a, b) {
+    chaptersLast(a, b) {
         const aType = a.getAttribute('data-type');
         const bType = b.getAttribute('data-type');
         if (aType === bType) {
@@ -45,22 +45,22 @@ const sortOperations = {
  */
 const moveActions = {
     up: {
-        active(elem, parent, book) {
+        active(elem, parent) {
             return !(elem.previousElementSibling === null && !parent);
         },
-        run(elem, parent, book) {
+        run(elem, parent) {
             const newSibling = elem.previousElementSibling || parent;
             newSibling.insertAdjacentElement('beforebegin', elem);
-        }
+        },
     },
     down: {
-        active(elem, parent, book) {
+        active(elem, parent) {
             return !(elem.nextElementSibling === null && !parent);
         },
-        run(elem, parent, book) {
+        run(elem, parent) {
             const newSibling = elem.nextElementSibling || parent;
             newSibling.insertAdjacentElement('afterend', elem);
-        }
+        },
     },
     next_book: {
         active(elem, parent, book) {
@@ -69,7 +69,7 @@ const moveActions = {
         run(elem, parent, book) {
             const newList = book.nextElementSibling.querySelector('ul');
             newList.prepend(elem);
-        }
+        },
     },
     prev_book: {
         active(elem, parent, book) {
@@ -78,13 +78,13 @@ const moveActions = {
         run(elem, parent, book) {
             const newList = book.previousElementSibling.querySelector('ul');
             newList.appendChild(elem);
-        }
+        },
     },
     next_chapter: {
-        active(elem, parent, book) {
+        active(elem, parent) {
             return elem.dataset.type === 'page' && this.getNextChapter(elem, parent);
         },
-        run(elem, parent, book) {
+        run(elem, parent) {
             const nextChapter = this.getNextChapter(elem, parent);
             nextChapter.querySelector('ul').prepend(elem);
         },
@@ -92,14 +92,14 @@ const moveActions = {
             const topLevel = (parent || elem);
             const topItems = Array.from(topLevel.parentElement.children);
             const index = topItems.indexOf(topLevel);
-            return topItems.slice(index + 1).find(elem => elem.dataset.type === 'chapter');
-        }
+            return topItems.slice(index + 1).find(item => item.dataset.type === 'chapter');
+        },
     },
     prev_chapter: {
-        active(elem, parent, book) {
+        active(elem, parent) {
             return elem.dataset.type === 'page' && this.getPrevChapter(elem, parent);
         },
-        run(elem, parent, book) {
+        run(elem, parent) {
             const prevChapter = this.getPrevChapter(elem, parent);
             prevChapter.querySelector('ul').append(elem);
         },
@@ -107,40 +107,40 @@ const moveActions = {
             const topLevel = (parent || elem);
             const topItems = Array.from(topLevel.parentElement.children);
             const index = topItems.indexOf(topLevel);
-            return topItems.slice(0, index).reverse().find(elem => elem.dataset.type === 'chapter');
-        }
+            return topItems.slice(0, index).reverse().find(item => item.dataset.type === 'chapter');
+        },
     },
     book_end: {
-        active(elem, parent, book) {
+        active(elem, parent) {
             return parent || (parent === null && elem.nextElementSibling);
         },
         run(elem, parent, book) {
             book.querySelector('ul').append(elem);
-        }
+        },
     },
     book_start: {
-        active(elem, parent, book) {
+        active(elem, parent) {
             return parent || (parent === null && elem.previousElementSibling);
         },
         run(elem, parent, book) {
             book.querySelector('ul').prepend(elem);
-        }
+        },
     },
     before_chapter: {
-        active(elem, parent, book) {
+        active(elem, parent) {
             return parent;
         },
-        run(elem, parent, book) {
+        run(elem, parent) {
             parent.insertAdjacentElement('beforebegin', elem);
-        }
+        },
     },
     after_chapter: {
-        active(elem, parent, book) {
+        active(elem, parent) {
             return parent;
         },
-        run(elem, parent, book) {
+        run(elem, parent) {
             parent.insertAdjacentElement('afterend', elem);
-        }
+        },
     },
 };
 
@@ -196,12 +196,12 @@ export class BookSort extends Component {
             reverse = (lastSort === sort) ? !reverse : false;
             let sortFunction = sortOperations[sort];
             if (reverse && reversibleTypes.includes(sort)) {
-                sortFunction = function(a, b) {
-                    return 0 - sortOperations[sort](a, b)
+                sortFunction = function reverseSortOperation(a, b) {
+                    return 0 - sortOperations[sort](a, b);
                 };
             }
 
-            for (let list of sortLists) {
+            for (const list of sortLists) {
                 const directItems = Array.from(list.children).filter(child => child.matches('li'));
                 directItems.sort(sortFunction).forEach(sortedItem => {
                     list.appendChild(sortedItem);
@@ -221,7 +221,7 @@ export class BookSort extends Component {
         const alreadyAdded = this.container.querySelector(`[data-type="book"][data-id="${entityInfo.id}"]`) !== null;
         if (alreadyAdded) return;
 
-        const entitySortItemUrl = entityInfo.link + '/sort-item';
+        const entitySortItemUrl = `${entityInfo.link}/sort-item`;
         window.$http.get(entitySortItemUrl).then(resp => {
             const newBookContainer = htmlToDom(resp.data);
             this.sortContainer.append(newBookContainer);
@@ -249,9 +249,9 @@ export class BookSort extends Component {
         const chapterGroupConfig = {
             name: 'chapter',
             pull: ['book', 'chapter'],
-            put: function(toList, fromList, draggedElem) {
+            put(toList, fromList, draggedElem) {
                 return draggedElem.getAttribute('data-type') === 'page';
-            }
+            },
         };
 
         for (const sortElem of sortElems) {
@@ -260,8 +260,8 @@ export class BookSort extends Component {
                 animation: 150,
                 fallbackOnBody: true,
                 swapThreshold: 0.65,
-                onSort: (event) => {
-                    this.ensureNoNestedChapters()
+                onSort: () => {
+                    this.ensureNoNestedChapters();
                     this.updateMapInput();
                     this.updateMoveActionStateForAll();
                 },
@@ -304,7 +304,7 @@ export class BookSort extends Component {
         const entityMap = [];
         const lists = this.container.querySelectorAll('.sort-list');
 
-        for (let list of lists) {
+        for (const list of lists) {
             const bookId = list.closest('[data-type="book"]').getAttribute('data-id');
             const directChildren = Array.from(list.children)
                 .filter(elem => elem.matches('[data-type="page"], [data-type="chapter"]'));
@@ -332,9 +332,9 @@ export class BookSort extends Component {
         entityMap.push({
             id: childId,
             sort: index,
-            parentChapter: parentChapter,
-            type: type,
-            book: bookId
+            parentChapter,
+            type,
+            book: bookId,
         });
 
         const subPages = childElem.querySelectorAll('[data-type="page"]');
@@ -344,7 +344,7 @@ export class BookSort extends Component {
                 sort: i,
                 parentChapter: childId,
                 type: 'page',
-                book: bookId
+                book: bookId,
             });
         }
     }
@@ -383,4 +383,5 @@ export class BookSort extends Component {
             this.updateMoveActionState(item);
         }
     }
-}
\ No newline at end of file
+
+}
index 37df213e3c98e1ce12fa2de347e9397c3213238f..7c6480a1af0b8a6c176e1f6dd8beca9ada8a5033 100644 (file)
@@ -1,5 +1,5 @@
-import {slideUp, slideDown} from "../services/animations";
-import {Component} from "./component";
+import {slideUp, slideDown} from '../services/animations';
+import {Component} from './component';
 
 export class ChapterContents extends Component {
 
@@ -27,6 +27,11 @@ export class ChapterContents extends Component {
 
     click(event) {
         event.preventDefault();
-        this.isOpen ?  this.close() : this.open();
+        if (this.isOpen) {
+            this.close();
+        } else {
+            this.open();
+        }
     }
+
 }
index 205cbd8fdbc21efec49301430a7eee97fa67a991..9cedd2314ff53555cb668129fb55e66aa419ac0d 100644 (file)
@@ -1,9 +1,19 @@
-import {onChildEvent, onEnterPress, onSelect} from "../services/dom";
-import {Component} from "./component";
-
+import {onChildEvent, onEnterPress, onSelect} from '../services/dom';
+import {Component} from './component';
 
 export class CodeEditor extends Component {
 
+    /**
+     * @type {null|SimpleEditorInterface}
+     */
+    editor = null;
+
+    callback = null;
+
+    history = {};
+
+    historyKey = 'code_history';
+
     setup() {
         this.container = this.$refs.container;
         this.popup = this.$el;
@@ -16,10 +26,6 @@ export class CodeEditor extends Component {
         this.historyList = this.$refs.historyList;
         this.favourites = new Set(this.$opts.favourites.split(','));
 
-        this.callback = null;
-        this.editor = null;
-        this.history = {};
-        this.historyKey = 'code_history';
         this.setupListeners();
         this.setupFavourites();
     }
@@ -37,15 +43,15 @@ export class CodeEditor extends Component {
             this.languageInputChange(language);
         });
 
-        onEnterPress(this.languageInput, e => this.save());
-        this.languageInput.addEventListener('input', e => this.languageInputChange(this.languageInput.value));
-        onSelect(this.saveButton, e => this.save());
+        onEnterPress(this.languageInput, () => this.save());
+        this.languageInput.addEventListener('input', () => this.languageInputChange(this.languageInput.value));
+        onSelect(this.saveButton, () => this.save());
 
         onChildEvent(this.historyList, 'button', 'click', (event, elem) => {
             event.preventDefault();
             const historyTime = elem.dataset.time;
             if (this.editor) {
-                this.editor.setValue(this.history[historyTime]);
+                this.editor.setContent(this.history[historyTime]);
             }
         });
     }
@@ -68,17 +74,18 @@ export class CodeEditor extends Component {
 
         onChildEvent(button.parentElement, '.lang-option-favorite-toggle', 'click', () => {
             isFavorite = !isFavorite;
-            isFavorite ? this.favourites.add(language) : this.favourites.delete(language);
+            const action = isFavorite ? this.favourites.add : this.favourites.delete;
+            action(language);
             button.setAttribute('data-favourite', isFavorite ? 'true' : 'false');
 
             window.$http.patch('/preferences/update-code-language-favourite', {
-                language: language,
-                active: isFavorite
+                language,
+                active: isFavorite,
             });
 
             this.sortLanguageList();
             if (isFavorite) {
-                button.scrollIntoView({block: "center", behavior: "smooth"});
+                button.scrollIntoView({block: 'center', behavior: 'smooth'});
             }
         });
     }
@@ -90,7 +97,7 @@ export class CodeEditor extends Component {
 
             if (aFav && !bFav) {
                 return -1;
-            } else if (bFav && !aFav) {
+            } if (bFav && !aFav) {
                 return 1;
             }
 
@@ -104,19 +111,18 @@ export class CodeEditor extends Component {
 
     save() {
         if (this.callback) {
-            this.callback(this.editor.getValue(), this.languageInput.value);
+            this.callback(this.editor.getContent(), this.languageInput.value);
         }
         this.hide();
     }
 
-    open(code, language, callback) {
+    async open(code, language, callback) {
         this.languageInput.value = language;
         this.callback = callback;
 
-        this.show()
-            .then(() => this.languageInputChange(language))
-            .then(() => window.importVersioned('code'))
-            .then(Code => Code.setContent(this.editor, code));
+        await this.show();
+        this.languageInputChange(language);
+        this.editor.setContent(code);
     }
 
     async show() {
@@ -127,10 +133,9 @@ export class CodeEditor extends Component {
 
         this.loadHistory();
         this.getPopup().show(() => {
-            Code.updateLayout(this.editor);
             this.editor.focus();
         }, () => {
-            this.addHistory()
+            this.addHistory();
         });
     }
 
@@ -147,8 +152,7 @@ export class CodeEditor extends Component {
     }
 
     async updateEditorMode(language) {
-        const Code = await window.importVersioned('code');
-        Code.setMode(this.editor, language, this.editor.getValue());
+        this.editor.setMode(language, this.editor.getContent());
     }
 
     languageInputChange(language) {
@@ -160,7 +164,7 @@ export class CodeEditor extends Component {
             const isMatch = inputLang === lang;
             link.classList.toggle('active', isMatch);
             if (isMatch) {
-                link.scrollIntoView({block: "center", behavior: "smooth"});
+                link.scrollIntoView({block: 'center', behavior: 'smooth'});
             }
         }
     }
@@ -170,14 +174,14 @@ export class CodeEditor extends Component {
         const historyKeys = Object.keys(this.history).reverse();
         this.historyDropDown.classList.toggle('hidden', historyKeys.length === 0);
         this.historyList.innerHTML = historyKeys.map(key => {
-             const localTime = (new Date(parseInt(key))).toLocaleTimeString();
-             return `<li><button type="button" data-time="${key}" class="text-item">${localTime}</button></li>`;
+            const localTime = (new Date(parseInt(key, 10))).toLocaleTimeString();
+            return `<li><button type="button" data-time="${key}" class="text-item">${localTime}</button></li>`;
         }).join('');
     }
 
     addHistory() {
         if (!this.editor) return;
-        const code = this.editor.getValue();
+        const code = this.editor.getContent();
         if (!code) return;
 
         // Stop if we'd be storing the same as the last item
@@ -189,4 +193,4 @@ export class CodeEditor extends Component {
         window.sessionStorage.setItem(this.historyKey, historyString);
     }
 
-}
\ No newline at end of file
+}
index 14bfc97f04ed5358b2758112e9cf3f06d47ae423..e12d770447fb1584243e0d3d9820ef21302d96a6 100644 (file)
@@ -1,6 +1,6 @@
-import {Component} from "./component";
+import {Component} from './component';
 
-export class CodeHighlighter extends Component{
+export class CodeHighlighter extends Component {
 
     setup() {
         const container = this.$el;
@@ -8,9 +8,9 @@ export class CodeHighlighter extends Component{
         const codeBlocks = container.querySelectorAll('pre');
         if (codeBlocks.length > 0) {
             window.importVersioned('code').then(Code => {
-               Code.highlightWithin(container);
+                Code.highlightWithin(container);
             });
         }
     }
 
-}
\ No newline at end of file
+}
index 0e49aec1755693c8615fa1447e37b1fe549b3545..2f536da0b7af692048f1bd9d3c89d6f4be693a7b 100644 (file)
@@ -2,14 +2,14 @@
  * A simple component to render a code editor within the textarea
  * this exists upon.
  */
-import {Component} from "./component";
+import {Component} from './component';
 
 export class CodeTextarea extends Component {
 
     async setup() {
-        const mode = this.$opts.mode;
+        const {mode} = this.$opts;
         const Code = await window.importVersioned('code');
         Code.inlineEditor(this.$el, mode);
     }
 
-}
\ No newline at end of file
+}
index bb8ed477ffe9f600769dace8ba6d7f2af32d5fb7..6f740ed7163204020fcbd6569393b14729d787c4 100644 (file)
@@ -1,5 +1,5 @@
-import {slideDown, slideUp} from "../services/animations";
-import {Component} from "./component";
+import {slideDown, slideUp} from '../services/animations';
+import {Component} from './component';
 
 /**
  * Collapsible
@@ -45,4 +45,4 @@ export class Collapsible extends Component {
         }
     }
 
-}
\ No newline at end of file
+}
index 292bbb62414581184b3eb7aaf2e28a98eaa0d627..654f41a96643ef750d0c63bf6dac3d32a3203a08 100644 (file)
@@ -51,8 +51,9 @@ export class Component {
         const componentName = this.$name;
         const event = new CustomEvent(`${componentName}-${eventName}`, {
             bubbles: true,
-            detail: data
+            detail: data,
         });
         this.$el.dispatchEvent(event);
     }
-}
\ No newline at end of file
+
+}
index 572945d5aba4ae7099a30dc29a28fe58f62d4542..184618fccfad9928cca02f81f3450b61fbe21ebe 100644 (file)
@@ -1,5 +1,5 @@
-import {onSelect} from "../services/dom";
-import {Component} from "./component";
+import {onSelect} from '../services/dom';
+import {Component} from './component';
 
 /**
  * Custom equivalent of window.confirm() using our popup component.
@@ -25,8 +25,8 @@ export class ConfirmDialog extends Component {
             this.sendResult(false);
         });
 
-        return new Promise((res, rej) => {
-           this.res = res;
+        return new Promise(res => {
+            this.res = res;
         });
     }
 
@@ -42,9 +42,9 @@ export class ConfirmDialog extends Component {
      */
     sendResult(result) {
         if (this.res) {
-            this.res(result)
+            this.res(result);
             this.res = null;
         }
     }
 
-}
\ No newline at end of file
+}
index 99804c4bcea010165ad4298f405860e904b9689d..a5f1d5664247c9763a520cbec034b20fc7ca666e 100644 (file)
@@ -1,4 +1,4 @@
-import {Component} from "./component";
+import {Component} from './component';
 
 export class CustomCheckbox extends Component {
 
@@ -30,4 +30,4 @@ export class CustomCheckbox extends Component {
         this.display.setAttribute('aria-checked', checked);
     }
 
-}
\ No newline at end of file
+}
index 6466fb584882b0af36280f06dc2420f7599305e8..71c2026294917abd63dc8b58ab54e3527eb99469 100644 (file)
@@ -1,4 +1,4 @@
-import {Component} from "./component";
+import {Component} from './component';
 
 export class DetailsHighlighter extends Component {
 
@@ -19,4 +19,5 @@ export class DetailsHighlighter extends Component {
         }
         this.dealtWith = true;
     }
-}
\ No newline at end of file
+
+}
index 30a2aadc1467ddd97a1a3ccb30efea591383f07b..2344619f5e9b794de3177c6c4acf8a5c7550b351 100644 (file)
@@ -1,6 +1,6 @@
-import {debounce} from "../services/util";
-import {transitionHeight} from "../services/animations";
-import {Component} from "./component";
+import {debounce} from '../services/util';
+import {transitionHeight} from '../services/animations';
+import {Component} from './component';
 
 export class DropdownSearch extends Component {
 
@@ -40,7 +40,7 @@ export class DropdownSearch extends Component {
 
     runLocalSearch(searchTerm) {
         const listItems = this.listContainerElem.querySelectorAll(this.localSearchSelector);
-        for (let listItem of listItems) {
+        for (const listItem of listItems) {
             const match = !searchTerm || listItem.textContent.toLowerCase().includes(searchTerm);
             listItem.style.display = match ? 'flex' : 'none';
             listItem.classList.toggle('hidden', !match);
@@ -79,4 +79,4 @@ export class DropdownSearch extends Component {
         this.loadingElem.style.display = show ? 'block' : 'none';
     }
 
-}
\ No newline at end of file
+}
index ed69088b29acae227a9f57557406299a74e71459..b68f332b6f13a04a2d2db7fd706cfae0d42dc8bd 100644 (file)
@@ -1,6 +1,6 @@
-import {onSelect} from "../services/dom";
-import {KeyboardNavigationHandler} from "../services/keyboard-navigation";
-import {Component} from "./component";
+import {onSelect} from '../services/dom';
+import {KeyboardNavigationHandler} from '../services/keyboard-navigation';
+import {Component} from './component';
 
 /**
  * Dropdown
@@ -41,7 +41,11 @@ export class Dropdown extends Component {
             this.menu.style.position = 'fixed';
             this.menu.style.width = `${menuOriginalRect.width}px`;
             this.menu.style.left = `${menuOriginalRect.left}px`;
-            heightOffset = dropUpwards ? (window.innerHeight - menuOriginalRect.top  - toggleHeight / 2) : menuOriginalRect.top;
+            if (dropUpwards) {
+                heightOffset = (window.innerHeight - menuOriginalRect.top - toggleHeight / 2);
+            } else {
+                heightOffset = menuOriginalRect.top;
+            }
         }
 
         // Adjust menu to display upwards if near the bottom of the screen
@@ -55,8 +59,8 @@ export class Dropdown extends Component {
 
         // Set listener to hide on mouse leave or window click
         this.menu.addEventListener('mouseleave', this.hide);
-        window.addEventListener('click', event => {
-            if (!this.menu.contains(event.target)) {
+        window.addEventListener('click', clickEvent => {
+            if (!this.menu.contains(clickEvent.target)) {
                 this.hide();
             }
         });
@@ -76,7 +80,7 @@ export class Dropdown extends Component {
     }
 
     hideAll() {
-        for (let dropdown of window.$components.get('dropdown')) {
+        for (const dropdown of window.$components.get('dropdown')) {
             dropdown.hide();
         }
     }
@@ -100,13 +104,13 @@ export class Dropdown extends Component {
     }
 
     setupListeners() {
-        const keyboardNavHandler = new KeyboardNavigationHandler(this.container, (event) => {
+        const keyboardNavHandler = new KeyboardNavigationHandler(this.container, event => {
             this.hide();
             this.toggle.focus();
             if (!this.bubbleEscapes) {
                 event.stopPropagation();
             }
-        }, (event) => {
+        }, event => {
             if (event.target.nodeName === 'INPUT') {
                 event.preventDefault();
                 event.stopPropagation();
@@ -120,10 +124,10 @@ export class Dropdown extends Component {
 
         // Hide menu on option click
         this.container.addEventListener('click', event => {
-             const possibleChildren = Array.from(this.menu.querySelectorAll('a'));
-             if (possibleChildren.includes(event.target)) {
-                 this.hide();
-             }
+            const possibleChildren = Array.from(this.menu.querySelectorAll('a'));
+            if (possibleChildren.includes(event.target)) {
+                this.hide();
+            }
         });
 
         onSelect(this.toggle, event => {
index 911a033c776b81ca757db2ad1e85875d599c0877..e7aae769e6b1ec3934ce94f9a69c97f63633443d 100644 (file)
@@ -1,8 +1,9 @@
-import DropZoneLib from "dropzone";
-import {fadeOut} from "../services/animations";
-import {Component} from "./component";
+import DropZoneLib from 'dropzone';
+import {fadeOut} from '../services/animations';
+import {Component} from './component';
 
 export class Dropzone extends Component {
+
     setup() {
         this.container = this.$el;
         this.url = this.$opts.url;
@@ -12,7 +13,7 @@ export class Dropzone extends Component {
         this.uploadLimitMessage = this.$opts.uploadLimitMessage;
         this.timeoutMessage = this.$opts.timeoutMessage;
 
-        const _this = this;
+        const component = this;
         this.dz = new DropZoneLib(this.container, {
             addRemoveLinks: true,
             dictRemoveFile: this.removeMessage,
@@ -22,22 +23,21 @@ export class Dropzone extends Component {
             withCredentials: true,
             init() {
                 this.dz = this;
-                this.dz.on('sending', _this.onSending.bind(_this));
-                this.dz.on('success', _this.onSuccess.bind(_this));
-                this.dz.on('error', _this.onError.bind(_this));
-            }
+                this.dz.on('sending', component.onSending.bind(component));
+                this.dz.on('success', component.onSuccess.bind(component));
+                this.dz.on('error', component.onError.bind(component));
+            },
         });
     }
 
     onSending(file, xhr, data) {
-
         const token = window.document.querySelector('meta[name=token]').getAttribute('content');
         data.append('_token', token);
 
-        xhr.ontimeout = (e) => {
+        xhr.ontimeout = () => {
             this.dz.emit('complete', file);
             this.dz.emit('error', file, this.timeoutMessage);
-        }
+        };
     }
 
     onSuccess(file, data) {
@@ -55,10 +55,10 @@ export class Dropzone extends Component {
     onError(file, errorMessage, xhr) {
         this.$emit('error', {file, errorMessage, xhr});
 
-        const setMessage = (message) => {
+        const setMessage = message => {
             const messsageEl = file.previewElement.querySelector('[data-dz-errormessage]');
             messsageEl.textContent = message;
-        }
+        };
 
         if (xhr && xhr.status === 413) {
             setMessage(this.uploadLimitMessage);
@@ -70,4 +70,5 @@ export class Dropzone extends Component {
     removeAll() {
         this.dz.removeAllFiles(true);
     }
-}
\ No newline at end of file
+
+}
index a581ae7b4609727200ad3251b6f0e5e59dd1213e..4d3c0ae75d22177e0362c21b2b2b7f971e1af2ac 100644 (file)
@@ -1,4 +1,4 @@
-import {Component} from "./component";
+import {Component} from './component';
 
 export class EditorToolbox extends Component {
 
@@ -35,11 +35,10 @@ export class EditorToolbox extends Component {
     }
 
     setActiveTab(tabName, openToolbox = false) {
-
         // Set button visibility
         for (const button of this.buttons) {
             button.classList.remove('active');
-            const bName =  button.dataset.tab;
+            const bName = button.dataset.tab;
             if (bName === tabName) button.classList.add('active');
         }
 
@@ -55,4 +54,4 @@ export class EditorToolbox extends Component {
         }
     }
 
-}
\ No newline at end of file
+}
index d4a616ff1d5765e2f278028016e8fa95bf1d8cbc..7ab99a2a70bb2f45445d485b6aff49e018b25d7b 100644 (file)
@@ -1,5 +1,5 @@
-import {htmlToDom} from "../services/dom";
-import {Component} from "./component";
+import {htmlToDom} from '../services/dom';
+import {Component} from './component';
 
 export class EntityPermissions extends Component {
 
@@ -29,12 +29,12 @@ export class EntityPermissions extends Component {
         this.container.addEventListener('click', event => {
             const button = event.target.closest('button');
             if (button && button.dataset.roleId) {
-                this.removeRowOnButtonClick(button)
+                this.removeRowOnButtonClick(button);
             }
         });
 
         // Role select change
-        this.roleSelect.addEventListener('change', event => {
+        this.roleSelect.addEventListener('change', () => {
             const roleId = this.roleSelect.value;
             if (roleId) {
                 this.addRoleRow(roleId);
@@ -61,8 +61,8 @@ export class EntityPermissions extends Component {
 
     removeRowOnButtonClick(button) {
         const row = button.closest('.item-list-row');
-        const roleId = button.dataset.roleId;
-        const roleName = button.dataset.roleName;
+        const {roleId} = button.dataset;
+        const {roleName} = button.dataset;
 
         const option = document.createElement('option');
         option.value = roleId;
@@ -72,4 +72,4 @@ export class EntityPermissions extends Component {
         row.remove();
     }
 
-}
\ No newline at end of file
+}
index b0e42401d51b1520807cfb97d295afaf27ecd826..7a50444708dee6313ab5b2caee5a2dd21def7cdb 100644 (file)
@@ -1,7 +1,8 @@
-import {onSelect} from "../services/dom";
-import {Component} from "./component";
+import {onSelect} from '../services/dom';
+import {Component} from './component';
 
 export class EntitySearch extends Component {
+
     setup() {
         this.entityId = this.$opts.entityId;
         this.entityType = this.$opts.entityType;
@@ -30,7 +31,8 @@ export class EntitySearch extends Component {
     runSearch() {
         const term = this.searchInput.value.trim();
         if (term.length === 0) {
-            return this.clearSearch();
+            this.clearSearch();
+            return;
         }
 
         this.searchView.classList.remove('hidden');
@@ -51,4 +53,5 @@ export class EntitySearch extends Component {
         this.loadingBlock.classList.add('hidden');
         this.searchInput.value = '';
     }
-}
\ No newline at end of file
+
+}
index d455f7ee7d5286f3bbfb9979ec0651ea6dfac98c..e21e67fb33ebb2b6e1ec914d30d425114857bce4 100644 (file)
@@ -1,4 +1,4 @@
-import {Component} from "./component";
+import {Component} from './component';
 
 export class EntitySelectorPopup extends Component {
 
@@ -57,4 +57,5 @@ export class EntitySelectorPopup extends Component {
         this.getSelector().reset();
         if (this.callback && entity) this.callback(entity);
     }
-}
\ No newline at end of file
+
+}
index 09d14b23383075d015a9c846679de9b6641bf188..f12108fbb497877d9d96cd345e66cdf6a24f51bf 100644 (file)
@@ -1,5 +1,5 @@
-import {onChildEvent} from "../services/dom";
-import {Component} from "./component";
+import {onChildEvent} from '../services/dom';
+import {Component} from './component';
 
 /**
  * Entity Selector
@@ -29,7 +29,7 @@ export class EntitySelector extends Component {
         this.elem.addEventListener('click', this.onClick.bind(this));
 
         let lastSearch = 0;
-        this.searchInput.addEventListener('input', event => {
+        this.searchInput.addEventListener('input', () => {
             lastSearch = Date.now();
             this.showLoading();
             setTimeout(() => {
@@ -43,35 +43,35 @@ export class EntitySelector extends Component {
         });
 
         // Keyboard navigation
-        onChildEvent(this.$el, '[data-entity-type]', 'keydown', (e, el) => {
-            if (e.ctrlKey && e.code === 'Enter') {
+        onChildEvent(this.$el, '[data-entity-type]', 'keydown', event => {
+            if (event.ctrlKey && event.code === 'Enter') {
                 const form = this.$el.closest('form');
                 if (form) {
                     form.submit();
-                    e.preventDefault();
+                    event.preventDefault();
                     return;
                 }
             }
 
-            if (e.code === 'ArrowDown') {
+            if (event.code === 'ArrowDown') {
                 this.focusAdjacent(true);
             }
-            if (e.code === 'ArrowUp') {
+            if (event.code === 'ArrowUp') {
                 this.focusAdjacent(false);
             }
         });
 
-        this.searchInput.addEventListener('keydown', e => {
-            if (e.code === 'ArrowDown') {
+        this.searchInput.addEventListener('keydown', event => {
+            if (event.code === 'ArrowDown') {
                 this.focusAdjacent(true);
             }
-        })
+        });
     }
 
     focusAdjacent(forward = true) {
         const items = Array.from(this.resultsContainer.querySelectorAll('[data-entity-type]'));
         const selectedIndex = items.indexOf(document.activeElement);
-        const newItem = items[selectedIndex+ (forward ? 1 : -1)] || items[0];
+        const newItem = items[selectedIndex + (forward ? 1 : -1)] || items[0];
         if (newItem) {
             newItem.focus();
         }
@@ -101,7 +101,7 @@ export class EntitySelector extends Component {
         window.$http.get(this.searchUrl()).then(resp => {
             this.resultsContainer.innerHTML = resp.data;
             this.hideLoading();
-        })
+        });
     }
 
     searchUrl() {
@@ -144,13 +144,13 @@ export class EntitySelector extends Component {
 
         const link = item.getAttribute('href');
         const name = item.querySelector('.entity-list-item-name').textContent;
-        const data = {id: Number(id), name: name, link: link};
+        const data = {id: Number(id), name, link};
 
         if (isSelected) {
             item.classList.add('selected');
             this.selectedItemData = data;
         } else {
-            window.$events.emit('entity-select-change', null)
+            window.$events.emit('entity-select-change', null);
         }
 
         if (!isDblClick && !isSelected) return;
@@ -159,7 +159,7 @@ export class EntitySelector extends Component {
             this.confirmSelection(data);
         }
         if (isSelected) {
-            window.$events.emit('entity-select-change', data)
+            window.$events.emit('entity-select-change', data);
         }
     }
 
@@ -175,4 +175,4 @@ export class EntitySelector extends Component {
         this.selectedItemData = null;
     }
 
-}
\ No newline at end of file
+}
index 2e6fd5fdbac008566f7fc88701000327853e7f0d..2097c0528868181cdc9e94c67cc630fdfca18652 100644 (file)
@@ -1,5 +1,5 @@
-import {onSelect} from "../services/dom";
-import {Component} from "./component";
+import {onSelect} from '../services/dom';
+import {Component} from './component';
 
 /**
  * EventEmitSelect
@@ -12,15 +12,15 @@ import {Component} from "./component";
  * All options will be set as the "detail" of the event with
  * their values included.
  */
-export class EventEmitSelect extends Component{
+export class EventEmitSelect extends Component {
+
     setup() {
         this.container = this.$el;
         this.name = this.$opts.name;
 
-
         onSelect(this.$el, () => {
             this.$emit(this.name, this.$opts);
         });
     }
 
-}
\ No newline at end of file
+}
index ab4d38ab1df2224270a27aab2f1fca6ae3786e10..0d2018b9da23531b27d98b18db682335ab47615b 100644 (file)
@@ -1,9 +1,9 @@
-import {slideUp, slideDown} from "../services/animations";
-import {Component} from "./component";
+import {slideUp, slideDown} from '../services/animations';
+import {Component} from './component';
 
 export class ExpandToggle extends Component {
 
-    setup(elem) {
+    setup() {
         this.targetSelector = this.$opts.targetSelector;
         this.isOpen = this.$opts.isOpen === 'true';
         this.updateEndpoint = this.$opts.updateEndpoint;
@@ -24,8 +24,9 @@ export class ExpandToggle extends Component {
         event.preventDefault();
 
         const matchingElems = document.querySelectorAll(this.targetSelector);
-        for (let match of matchingElems) {
-            this.isOpen ?  this.close(match) : this.open(match);
+        for (const match of matchingElems) {
+            const action = this.isOpen ? this.close : this.open;
+            action(match);
         }
 
         this.isOpen = !this.isOpen;
@@ -34,8 +35,8 @@ export class ExpandToggle extends Component {
 
     updateSystemAjax(isOpen) {
         window.$http.patch(this.updateEndpoint, {
-            expand: isOpen ? 'true' : 'false'
+            expand: isOpen ? 'true' : 'false',
         });
     }
 
-}
\ No newline at end of file
+}
index 7bc8a1d45187f8f711f0362c15bcab42119996db..798bd7aacb0d5c00fb5d0ece92c6ae604678a235 100644 (file)
@@ -1,7 +1,7 @@
-import {htmlToDom} from "../services/dom";
-import {debounce} from "../services/util";
-import {KeyboardNavigationHandler} from "../services/keyboard-navigation";
-import {Component} from "./component";
+import {htmlToDom} from '../services/dom';
+import {debounce} from '../services/util';
+import {KeyboardNavigationHandler} from '../services/keyboard-navigation';
+import {Component} from './component';
 
 /**
  * Global (header) search box handling.
@@ -25,12 +25,12 @@ export class GlobalSearch extends Component {
 
         // Handle search input changes
         this.input.addEventListener('input', () => {
-            const value = this.input.value;
+            const {value} = this.input;
             if (value.length > 0) {
                 this.loadingWrap.style.display = 'block';
                 this.suggestionResultsWrap.style.opacity = '0.5';
                 updateSuggestionsDebounced(value);
-            }  else {
+            } else {
                 this.hideSuggestions();
             }
         });
@@ -55,7 +55,7 @@ export class GlobalSearch extends Component {
         if (!this.input.value) {
             return;
         }
-        
+
         const resultDom = htmlToDom(results);
 
         this.suggestionResultsWrap.innerHTML = '';
@@ -71,7 +71,7 @@ export class GlobalSearch extends Component {
         this.container.classList.add('search-active');
         window.requestAnimationFrame(() => {
             this.suggestions.classList.add('search-suggestions-animation');
-        })
+        });
     }
 
     hideSuggestions() {
@@ -79,4 +79,5 @@ export class GlobalSearch extends Component {
         this.suggestions.classList.remove('search-suggestions-animation');
         this.suggestionResultsWrap.innerHTML = '';
     }
-}
\ No newline at end of file
+
+}
index 11b23cca6cc06f833d8d3146ceb6c0bb0ddf6bdf..f94f897f617e5cfae3b39ddb3aaf7db073426d56 100644 (file)
@@ -1,4 +1,4 @@
-import {Component} from "./component";
+import {Component} from './component';
 
 export class HeaderMobileToggle extends Component {
 
@@ -19,10 +19,10 @@ export class HeaderMobileToggle extends Component {
         this.toggleButton.setAttribute('aria-expanded', this.open ? 'true' : 'false');
         if (this.open) {
             this.elem.addEventListener('keydown', this.onKeyDown);
-            window.addEventListener('click', this.onWindowClick)
+            window.addEventListener('click', this.onWindowClick);
         } else {
             this.elem.removeEventListener('keydown', this.onKeyDown);
-            window.removeEventListener('click', this.onWindowClick)
+            window.removeEventListener('click', this.onWindowClick);
         }
         event.stopPropagation();
     }
@@ -37,4 +37,4 @@ export class HeaderMobileToggle extends Component {
         this.onToggle(event);
     }
 
-}
\ No newline at end of file
+}
index 418b7c98a2be0f16801f7b0ac7e45536c6ff8faa..3cb99f0e208413290b7dff9565151adec76296d6 100644 (file)
@@ -1,5 +1,7 @@
-import {onChildEvent, onSelect, removeLoading, showLoading} from "../services/dom";
-import {Component} from "./component";
+import {
+    onChildEvent, onSelect, removeLoading, showLoading,
+} from '../services/dom';
+import {Component} from './component';
 
 export class ImageManager extends Component {
 
@@ -48,20 +50,20 @@ export class ImageManager extends Component {
             event.preventDefault();
         });
 
-        onSelect(this.cancelSearch, event => {
+        onSelect(this.cancelSearch, () => {
             this.resetListView();
             this.resetSearchView();
             this.loadGallery();
             this.cancelSearch.classList.remove('active');
         });
 
-        this.searchInput.addEventListener('input', event => {
+        this.searchInput.addEventListener('input', () => {
             this.cancelSearch.classList.toggle('active', this.searchInput.value.trim());
         });
 
         onChildEvent(this.listContainer, '.load-more', 'click', async event => {
             showLoading(event.target);
-            this.page++;
+            this.page += 1;
             await this.loadGallery();
             event.target.remove();
         });
@@ -69,7 +71,7 @@ export class ImageManager extends Component {
         this.listContainer.addEventListener('event-emit-select-image', this.onImageSelectEvent.bind(this));
 
         this.listContainer.addEventListener('error', event => {
-            event.target.src = baseUrl('loading_error.png');
+            event.target.src = window.baseUrl('loading_error.png');
         }, true);
 
         onSelect(this.selectButton, () => {
@@ -79,7 +81,7 @@ export class ImageManager extends Component {
             this.hide();
         });
 
-        onChildEvent(this.formContainer, '#image-manager-delete', 'click', event => {
+        onChildEvent(this.formContainer, '#image-manager-delete', 'click', () => {
             if (this.lastSelected) {
                 this.loadImageEditForm(this.lastSelected.id, true);
             }
@@ -210,4 +212,4 @@ export class ImageManager extends Component {
         window.$components.init(this.formContainer);
     }
 
-}
\ No newline at end of file
+}
index 03d9567d22e331c82a04c75bc1840e207c5d1eec..d25e01dd7b722835718ea54b310ba219c272f5ce 100644 (file)
@@ -1,4 +1,4 @@
-import {Component} from "./component";
+import {Component} from './component';
 
 export class ImagePicker extends Component {
 
@@ -31,7 +31,7 @@ export class ImagePicker extends Component {
             this.removeInput.setAttribute('disabled', 'disabled');
         }
 
-        for (let file of this.imageInput.files) {
+        for (const file of this.imageInput.files) {
             this.imageElem.src = window.URL.createObjectURL(file);
         }
         this.imageElem.classList.remove('none');
@@ -54,4 +54,4 @@ export class ImagePicker extends Component {
         this.resetInput.setAttribute('disabled', 'disabled');
     }
 
-}
\ No newline at end of file
+}
index 82136184b4eaa84f5095c3fe85fd1b457bb5d592..803714e62a35658d3b134831eb896740d2687bfb 100644 (file)
@@ -1,59 +1,59 @@
-export {AddRemoveRows} from "./add-remove-rows.js"
-export {AjaxDeleteRow} from "./ajax-delete-row.js"
-export {AjaxForm} from "./ajax-form.js"
-export {Attachments} from "./attachments.js"
-export {AttachmentsList} from "./attachments-list.js"
-export {AutoSuggest} from "./auto-suggest.js"
-export {AutoSubmit} from "./auto-submit.js"
-export {BackToTop} from "./back-to-top.js"
-export {BookSort} from "./book-sort.js"
-export {ChapterContents} from "./chapter-contents.js"
-export {CodeEditor} from "./code-editor.js"
-export {CodeHighlighter} from "./code-highlighter.js"
-export {CodeTextarea} from "./code-textarea.js"
-export {Collapsible} from "./collapsible.js"
-export {ConfirmDialog} from "./confirm-dialog"
-export {CustomCheckbox} from "./custom-checkbox.js"
-export {DetailsHighlighter} from "./details-highlighter.js"
-export {Dropdown} from "./dropdown.js"
-export {DropdownSearch} from "./dropdown-search.js"
-export {Dropzone} from "./dropzone.js"
-export {EditorToolbox} from "./editor-toolbox.js"
-export {EntityPermissions} from "./entity-permissions"
-export {EntitySearch} from "./entity-search.js"
-export {EntitySelector} from "./entity-selector.js"
-export {EntitySelectorPopup} from "./entity-selector-popup.js"
-export {EventEmitSelect} from "./event-emit-select.js"
-export {ExpandToggle} from "./expand-toggle.js"
-export {GlobalSearch} from "./global-search.js"
-export {HeaderMobileToggle} from "./header-mobile-toggle.js"
-export {ImageManager} from "./image-manager.js"
-export {ImagePicker} from "./image-picker.js"
-export {ListSortControl} from "./list-sort-control.js"
-export {MarkdownEditor} from "./markdown-editor.js"
-export {NewUserPassword} from "./new-user-password.js"
-export {Notification} from "./notification.js"
-export {OptionalInput} from "./optional-input.js"
-export {PageComments} from "./page-comments.js"
-export {PageDisplay} from "./page-display.js"
-export {PageEditor} from "./page-editor.js"
-export {PagePicker} from "./page-picker.js"
-export {PermissionsTable} from "./permissions-table.js"
-export {Pointer} from "./pointer.js"
-export {Popup} from "./popup.js"
-export {SettingAppColorScheme} from "./setting-app-color-scheme.js"
-export {SettingColorPicker} from "./setting-color-picker.js"
-export {SettingHomepageControl} from "./setting-homepage-control.js"
-export {ShelfSort} from "./shelf-sort.js"
-export {Shortcuts} from "./shortcuts"
-export {ShortcutInput} from "./shortcut-input"
-export {SortableList} from "./sortable-list.js"
-export {SubmitOnChange} from "./submit-on-change.js"
-export {Tabs} from "./tabs.js"
-export {TagManager} from "./tag-manager.js"
-export {TemplateManager} from "./template-manager.js"
-export {ToggleSwitch} from "./toggle-switch.js"
-export {TriLayout} from "./tri-layout.js"
-export {UserSelect} from "./user-select.js"
-export {WebhookEvents} from "./webhook-events"
-export {WysiwygEditor} from "./wysiwyg-editor.js"
+export {AddRemoveRows} from './add-remove-rows';
+export {AjaxDeleteRow} from './ajax-delete-row';
+export {AjaxForm} from './ajax-form';
+export {Attachments} from './attachments';
+export {AttachmentsList} from './attachments-list';
+export {AutoSuggest} from './auto-suggest';
+export {AutoSubmit} from './auto-submit';
+export {BackToTop} from './back-to-top';
+export {BookSort} from './book-sort';
+export {ChapterContents} from './chapter-contents';
+export {CodeEditor} from './code-editor';
+export {CodeHighlighter} from './code-highlighter';
+export {CodeTextarea} from './code-textarea';
+export {Collapsible} from './collapsible';
+export {ConfirmDialog} from './confirm-dialog';
+export {CustomCheckbox} from './custom-checkbox';
+export {DetailsHighlighter} from './details-highlighter';
+export {Dropdown} from './dropdown';
+export {DropdownSearch} from './dropdown-search';
+export {Dropzone} from './dropzone';
+export {EditorToolbox} from './editor-toolbox';
+export {EntityPermissions} from './entity-permissions';
+export {EntitySearch} from './entity-search';
+export {EntitySelector} from './entity-selector';
+export {EntitySelectorPopup} from './entity-selector-popup';
+export {EventEmitSelect} from './event-emit-select';
+export {ExpandToggle} from './expand-toggle';
+export {GlobalSearch} from './global-search';
+export {HeaderMobileToggle} from './header-mobile-toggle';
+export {ImageManager} from './image-manager';
+export {ImagePicker} from './image-picker';
+export {ListSortControl} from './list-sort-control';
+export {MarkdownEditor} from './markdown-editor';
+export {NewUserPassword} from './new-user-password';
+export {Notification} from './notification';
+export {OptionalInput} from './optional-input';
+export {PageComments} from './page-comments';
+export {PageDisplay} from './page-display';
+export {PageEditor} from './page-editor';
+export {PagePicker} from './page-picker';
+export {PermissionsTable} from './permissions-table';
+export {Pointer} from './pointer';
+export {Popup} from './popup';
+export {SettingAppColorScheme} from './setting-app-color-scheme';
+export {SettingColorPicker} from './setting-color-picker';
+export {SettingHomepageControl} from './setting-homepage-control';
+export {ShelfSort} from './shelf-sort';
+export {Shortcuts} from './shortcuts';
+export {ShortcutInput} from './shortcut-input';
+export {SortableList} from './sortable-list';
+export {SubmitOnChange} from './submit-on-change';
+export {Tabs} from './tabs';
+export {TagManager} from './tag-manager';
+export {TemplateManager} from './template-manager';
+export {ToggleSwitch} from './toggle-switch';
+export {TriLayout} from './tri-layout';
+export {UserSelect} from './user-select';
+export {WebhookEvents} from './webhook-events';
+export {WysiwygEditor} from './wysiwyg-editor';
index b8d4de73a0e7b8cd3326ca9e30679366043af7be..4b38fc1e50a5ca29528a1465ff75c16fb9d2aa1f 100644 (file)
@@ -2,7 +2,7 @@
  * ListSortControl
  * Manages the logic for the control which provides list sorting options.
  */
-import {Component} from "./component";
+import {Component} from './component';
 
 export class ListSortControl extends Component {
 
@@ -45,4 +45,4 @@ export class ListSortControl extends Component {
         this.form.submit();
     }
 
-}
\ No newline at end of file
+}
index 5cd92cae2bf1b7c556b2f947d5b0f2f360be3ba6..fa06807a5c815c79ebc117a3c9436940f913ee0d 100644 (file)
@@ -1,6 +1,5 @@
-import {debounce} from "../services/util";
-import {Component} from "./component";
-import {init as initEditor} from "../markdown/editor";
+import {Component} from './component';
+import {init as initEditor} from '../markdown/editor';
 
 export class MarkdownEditor extends Component {
 
@@ -17,7 +16,7 @@ export class MarkdownEditor extends Component {
         this.divider = this.$refs.divider;
         this.displayWrap = this.$refs.displayWrap;
 
-        const settingContainer = this.$refs.settingContainer;
+        const {settingContainer} = this.$refs;
         const settingInputs = settingContainer.querySelectorAll('input[type="checkbox"]');
 
         this.editor = null;
@@ -45,19 +44,18 @@ export class MarkdownEditor extends Component {
         window.$events.emitPublic(this.elem, 'editor-markdown::setup', {
             markdownIt: this.editor.markdown.getRenderer(),
             displayEl: this.display,
-            codeMirrorInstance: this.editor.cm,
+            cmEditorView: this.editor.cm,
         });
     }
 
     setupListeners() {
-
         // Button actions
         this.elem.addEventListener('click', event => {
-            let button = event.target.closest('button[data-action]');
+            const button = event.target.closest('button[data-action]');
             if (button === null) return;
 
             const action = button.getAttribute('data-action');
-            if (action === 'insertImage') this.editor.actions.insertImage();
+            if (action === 'insertImage') this.editor.actions.showImageInsert();
             if (action === 'insertLink') this.editor.actions.showLinkSelector();
             if (action === 'insertDrawing' && (event.ctrlKey || event.metaKey)) {
                 this.editor.actions.showImageManager();
@@ -80,29 +78,23 @@ export class MarkdownEditor extends Component {
             toolbarLabel.closest('.markdown-editor-wrap').classList.add('active');
         });
 
-        // Refresh CodeMirror on container resize
-        const resizeDebounced = debounce(() => this.editor.cm.refresh(), 100, false);
-        const observer = new ResizeObserver(resizeDebounced);
-        observer.observe(this.elem);
-
         this.handleDividerDrag();
     }
 
     handleDividerDrag() {
-        this.divider.addEventListener('pointerdown', event => {
+        this.divider.addEventListener('pointerdown', () => {
             const wrapRect = this.elem.getBoundingClientRect();
-            const moveListener = (event) => {
+            const moveListener = event => {
                 const xRel = event.pageX - wrapRect.left;
                 const xPct = Math.min(Math.max(20, Math.floor((xRel / wrapRect.width) * 100)), 80);
-                this.displayWrap.style.flexBasis = `${100-xPct}%`;
+                this.displayWrap.style.flexBasis = `${100 - xPct}%`;
                 this.editor.settings.set('editorWidth', xPct);
             };
-            const upListener = (event) => {
+            const upListener = () => {
                 window.removeEventListener('pointermove', moveListener);
                 window.removeEventListener('pointerup', upListener);
                 this.display.style.pointerEvents = null;
                 document.body.style.userSelect = null;
-                this.editor.cm.refresh();
             };
 
             this.display.style.pointerEvents = 'none';
@@ -112,7 +104,7 @@ export class MarkdownEditor extends Component {
         });
         const widthSetting = this.editor.settings.get('editorWidth');
         if (widthSetting) {
-            this.displayWrap.style.flexBasis = `${100-widthSetting}%`;
+            this.displayWrap.style.flexBasis = `${100 - widthSetting}%`;
         }
     }
 
index a4ed4d15b300c5e255e86ef43eee9f8c7b31d014..e294f8e97f3228ed5a111614bd0c697ff7323b81 100644 (file)
@@ -1,4 +1,4 @@
-import {Component} from "./component";
+import {Component} from './component';
 
 export class NewUserPassword extends Component {
 
@@ -23,4 +23,4 @@ export class NewUserPassword extends Component {
         this.inputContainer.style.display = inviting ? 'none' : 'block';
     }
 
-}
\ No newline at end of file
+}
index 8a0876241fe15232b82264e93556357691816614..8a1ef922a97a5870d2cf0683204a2e46e3769631 100644 (file)
@@ -1,13 +1,13 @@
-import {Component} from "./component";
+import {Component} from './component';
 
-export class Notification  extends Component {
+export class Notification extends Component {
 
     setup() {
         this.container = this.$el;
         this.type = this.$opts.type;
         this.textElem = this.container.querySelector('span');
         this.autoHide = this.$opts.autoHide === 'true';
-        this.initialShow = this.$opts.show === 'true'
+        this.initialShow = this.$opts.show === 'true';
         this.container.style.display = 'grid';
 
         window.$events.listen(this.type, text => {
@@ -47,4 +47,4 @@ export class Notification  extends Component {
         this.container.removeEventListener('transitionend', this.hideCleanup);
     }
 
-}
\ No newline at end of file
+}
index cc429c991522bcd9b51a5fc811127e7775a4a066..64cee12cd29b6a212a2fb882be6a0be193598a46 100644 (file)
@@ -1,7 +1,8 @@
-import {onSelect} from "../services/dom";
-import {Component} from "./component";
+import {onSelect} from '../services/dom';
+import {Component} from './component';
 
 export class OptionalInput extends Component {
+
     setup() {
         this.removeButton = this.$refs.remove;
         this.showButton = this.$refs.show;
@@ -24,4 +25,4 @@ export class OptionalInput extends Component {
         });
     }
 
-}
\ No newline at end of file
+}
index 726531e951f86f4a8447b41844803eb1d01dd083..0ac9d05720de80cdf9ec1b71e31d4ed02c77d1e0 100644 (file)
@@ -1,6 +1,6 @@
-import {scrollAndHighlightElement} from "../services/util";
-import {Component} from "./component";
-import {htmlToDom} from "../services/dom";
+import {scrollAndHighlightElement} from '../services/util';
+import {Component} from './component';
+import {htmlToDom} from '../services/dom';
 
 export class PageComments extends Component {
 
@@ -36,11 +36,11 @@ export class PageComments extends Component {
     }
 
     handleAction(event) {
-        let actionElem = event.target.closest('[action]');
+        const actionElem = event.target.closest('[action]');
 
         if (event.target.matches('a[href^="#"]')) {
             const id = event.target.href.split('#')[1];
-            scrollAndHighlightElement(document.querySelector('#' + id));
+            scrollAndHighlightElement(document.querySelector(`#${id}`));
         }
 
         if (actionElem === null) return;
@@ -68,24 +68,24 @@ export class PageComments extends Component {
         if (this.editingComment) this.closeUpdateForm();
         commentElem.querySelector('[comment-content]').style.display = 'none';
         commentElem.querySelector('[comment-edit-container]').style.display = 'block';
-        let textArea = commentElem.querySelector('[comment-edit-container] textarea');
-        let lineCount = textArea.value.split('\n').length;
-        textArea.style.height = ((lineCount * 20) + 40) + 'px';
+        const textArea = commentElem.querySelector('[comment-edit-container] textarea');
+        const lineCount = textArea.value.split('\n').length;
+        textArea.style.height = `${(lineCount * 20) + 40}px`;
         this.editingComment = commentElem;
     }
 
     updateComment(event) {
-        let form = event.target;
+        const form = event.target;
         event.preventDefault();
-        let text = form.querySelector('textarea').value;
-        let reqData = {
-            text: text,
+        const text = form.querySelector('textarea').value;
+        const reqData = {
+            text,
             parent_id: this.parentId || null,
         };
         this.showLoading(form);
-        let commentId = this.editingComment.getAttribute('comment');
+        const commentId = this.editingComment.getAttribute('comment');
         window.$http.put(`/comment/${commentId}`, reqData).then(resp => {
-            let newComment = document.createElement('div');
+            const newComment = document.createElement('div');
             newComment.innerHTML = resp.data;
             this.editingComment.innerHTML = newComment.children[0].innerHTML;
             window.$events.success(this.updatedText);
@@ -98,9 +98,9 @@ export class PageComments extends Component {
     }
 
     deleteComment(commentElem) {
-        let id = commentElem.getAttribute('comment');
+        const id = commentElem.getAttribute('comment');
         this.showLoading(commentElem.querySelector('[comment-content]'));
-        window.$http.delete(`/comment/${id}`).then(resp => {
+        window.$http.delete(`/comment/${id}`).then(() => {
             commentElem.parentNode.removeChild(commentElem);
             window.$events.success(this.deletedText);
             this.updateCount();
@@ -111,9 +111,9 @@ export class PageComments extends Component {
     saveComment(event) {
         event.preventDefault();
         event.stopPropagation();
-        let text = this.formInput.value;
-        let reqData = {
-            text: text,
+        const text = this.formInput.value;
+        const reqData = {
+            text,
             parent_id: this.parentId || null,
         };
         this.showLoading(this.form);
@@ -131,7 +131,7 @@ export class PageComments extends Component {
     }
 
     updateCount() {
-        let count = this.container.children.length;
+        const count = this.container.children.length;
         this.elem.querySelector('[comments-title]').textContent = window.trans_plural(this.countText, count, {count});
     }
 
@@ -148,14 +148,14 @@ export class PageComments extends Component {
         this.formContainer.parentNode.style.display = 'block';
         this.addButtonContainer.style.display = 'none';
         this.formInput.focus();
-        this.formInput.scrollIntoView({behavior: "smooth"});
+        this.formInput.scrollIntoView({behavior: 'smooth'});
     }
 
     hideForm() {
         this.formContainer.style.display = 'none';
         this.formContainer.parentNode.style.display = 'none';
         if (this.getCommentCount() > 0) {
-            this.elem.appendChild(this.addButtonContainer)
+            this.elem.appendChild(this.addButtonContainer);
         } else {
             this.commentCountBar.appendChild(this.addButtonContainer);
         }
@@ -182,7 +182,7 @@ export class PageComments extends Component {
 
     showLoading(formElem) {
         const groups = formElem.querySelectorAll('.form-group');
-        for (let group of groups) {
+        for (const group of groups) {
             group.style.display = 'none';
         }
         formElem.querySelector('.form-group.loading').style.display = 'block';
@@ -190,10 +190,10 @@ export class PageComments extends Component {
 
     hideLoading(formElem) {
         const groups = formElem.querySelectorAll('.form-group');
-        for (let group of groups) {
+        for (const group of groups) {
             group.style.display = 'block';
         }
         formElem.querySelector('.form-group.loading').style.display = 'none';
     }
 
-}
\ No newline at end of file
+}
index c06c3310dee2c44c221b331c8a78b8d3eee9d3f3..eb7df5fb6460b37b52835a719d78df7f11d28aaa 100644 (file)
@@ -1,6 +1,33 @@
-import * as DOM from "../services/dom";
-import {scrollAndHighlightElement} from "../services/util";
-import {Component} from "./component";
+import * as DOM from '../services/dom';
+import {scrollAndHighlightElement} from '../services/util';
+import {Component} from './component';
+
+function toggleAnchorHighlighting(elementId, shouldHighlight) {
+    DOM.forEach(`a[href="#${elementId}"]`, anchor => {
+        anchor.closest('li').classList.toggle('current-heading', shouldHighlight);
+    });
+}
+
+function headingVisibilityChange(entries) {
+    for (const entry of entries) {
+        const isVisible = (entry.intersectionRatio === 1);
+        toggleAnchorHighlighting(entry.target.id, isVisible);
+    }
+}
+
+function addNavObserver(headings) {
+    // Setup the intersection observer.
+    const intersectOpts = {
+        rootMargin: '0px 0px 0px 0px',
+        threshold: 1.0,
+    };
+    const pageNavObserver = new IntersectionObserver(headingVisibilityChange, intersectOpts);
+
+    // observe each heading
+    for (const heading of headings) {
+        pageNavObserver.observe(heading);
+    }
+}
 
 export class PageDisplay extends Component {
 
@@ -26,7 +53,7 @@ export class PageDisplay extends Component {
                 window.$components.first('tri-layout').showContent();
                 const contentId = child.getAttribute('href').substr(1);
                 this.goToText(contentId);
-                window.history.pushState(null, null, '#' + contentId);
+                window.history.pushState(null, null, `#${contentId}`);
             });
         }
     }
@@ -58,33 +85,6 @@ export class PageDisplay extends Component {
         if (headings.length > 0 && pageNav !== null) {
             addNavObserver(headings);
         }
-
-        function addNavObserver(headings) {
-            // Setup the intersection observer.
-            const intersectOpts = {
-                rootMargin: '0px 0px 0px 0px',
-                threshold: 1.0
-            };
-            const pageNavObserver = new IntersectionObserver(headingVisibilityChange, intersectOpts);
-
-            // observe each heading
-            for (const heading of headings) {
-                pageNavObserver.observe(heading);
-            }
-        }
-
-        function headingVisibilityChange(entries, observer) {
-            for (const entry of entries) {
-                const isVisible = (entry.intersectionRatio === 1);
-                toggleAnchorHighlighting(entry.target.id, isVisible);
-            }
-        }
-
-        function toggleAnchorHighlighting(elementId, shouldHighlight) {
-            DOM.forEach('a[href="#' + elementId + '"]', anchor => {
-                anchor.closest('li').classList.toggle('current-heading', shouldHighlight);
-            });
-        }
     }
 
     setupDetailsCodeBlockRefresh() {
@@ -96,4 +96,5 @@ export class PageDisplay extends Component {
         const details = [...this.container.querySelectorAll('details')];
         details.forEach(detail => detail.addEventListener('toggle', onToggle));
     }
-}
\ No newline at end of file
+
+}
index c58f45b66198544c4ffd0f8d21052d95a09ea1ae..e7f4c0ba95907345e7ad25b6b0fc2fa3284c8853 100644 (file)
@@ -1,9 +1,10 @@
-import * as Dates from "../services/dates";
-import {onSelect} from "../services/dom";
-import {debounce} from "../services/util";
-import {Component} from "./component";
+import * as Dates from '../services/dates';
+import {onSelect} from '../services/dom';
+import {debounce} from '../services/util';
+import {Component} from './component';
 
 export class PageEditor extends Component {
+
     setup() {
         // Options
         this.draftsEnabled = this.$opts.draftsEnabled === 'true';
@@ -22,7 +23,7 @@ export class PageEditor extends Component {
         this.draftDisplayIcon = this.$refs.draftDisplayIcon;
         this.changelogInput = this.$refs.changelogInput;
         this.changelogDisplay = this.$refs.changelogDisplay;
-        this.changeEditorButtons = this.$manyRefs.changeEditor;
+        this.changeEditorButtons = this.$manyRefs.changeEditor || [];
         this.switchDialogContainer = this.$refs.switchDialog;
 
         // Translations
@@ -58,7 +59,9 @@ export class PageEditor extends Component {
         window.$events.listen('editor-save-page', this.savePage.bind(this));
 
         // Listen to content changes from the editor
-        const onContentChange = () => this.autoSave.pendingChange = true;
+        const onContentChange = () => {
+            this.autoSave.pendingChange = true;
+        };
         window.$events.listen('editor-html-change', onContentChange);
         window.$events.listen('editor-markdown-change', onContentChange);
 
@@ -79,7 +82,8 @@ export class PageEditor extends Component {
 
     setInitialFocus() {
         if (this.hasDefaultTitle) {
-            return this.titleElem.select();
+            this.titleElem.select();
+            return;
         }
 
         window.setTimeout(() => {
@@ -93,12 +97,12 @@ export class PageEditor extends Component {
 
     runAutoSave() {
         // Stop if manually saved recently to prevent bombarding the server
-        const savedRecently = (Date.now() - this.autoSave.last < (this.autoSave.frequency)/2);
+        const savedRecently = (Date.now() - this.autoSave.last < (this.autoSave.frequency) / 2);
         if (savedRecently || !this.autoSave.pendingChange) {
             return;
         }
 
-        this.saveDraft()
+        this.saveDraft();
     }
 
     savePage() {
@@ -132,7 +136,9 @@ export class PageEditor extends Component {
             try {
                 const saveKey = `draft-save-fail-${(new Date()).toISOString()}`;
                 window.localStorage.setItem(saveKey, JSON.stringify(data));
-            } catch (err) {}
+            } catch (lsErr) {
+                console.error(lsErr);
+            }
 
             window.$events.emit('error', this.autosaveFailText);
         }
@@ -153,7 +159,8 @@ export class PageEditor extends Component {
         try {
             response = await window.$http.get(`/ajax/page/${this.pageId}`);
         } catch (e) {
-            return console.error(e);
+            console.error(e);
+            return;
         }
 
         if (this.autoSave.interval) {
@@ -172,7 +179,6 @@ export class PageEditor extends Component {
             this.startAutoSave();
         }, 1000);
         window.$events.emit('success', this.draftDiscardedText);
-
     }
 
     updateChangelogDisplay() {
@@ -180,7 +186,7 @@ export class PageEditor extends Component {
         if (summary.length === 0) {
             summary = this.setChangelogText;
         } else if (summary.length > 16) {
-            summary = summary.slice(0, 16) + '...';
+            summary = `${summary.slice(0, 16)}...`;
         }
         this.changelogDisplay.innerText = summary;
     }
@@ -193,7 +199,7 @@ export class PageEditor extends Component {
         event.preventDefault();
 
         const link = event.target.closest('a').href;
-        /** @var {ConfirmDialog} **/
+        /** @var {ConfirmDialog} * */
         const dialog = window.$components.firstOnElement(this.switchDialogContainer, 'confirm-dialog');
         const [saved, confirmed] = await Promise.all([this.saveDraft(), dialog.show()]);
 
index fba0a0a43f779a8802c0a7130789fb948f2e0226..130972fdd63ebc8aaed1cf4572a728d25a505904 100644 (file)
@@ -1,4 +1,8 @@
-import {Component} from "./component";
+import {Component} from './component';
+
+function toggleElem(elem, show) {
+    elem.style.display = show ? null : 'none';
+}
 
 export class PagePicker extends Component {
 
@@ -18,13 +22,13 @@ export class PagePicker extends Component {
         this.selectButton.addEventListener('click', this.showPopup.bind(this));
         this.display.parentElement.addEventListener('click', this.showPopup.bind(this));
 
-        this.resetButton.addEventListener('click', event => {
+        this.resetButton.addEventListener('click', () => {
             this.setValue('', '');
         });
     }
 
     showPopup() {
-        /** @type {EntitySelectorPopup} **/
+        /** @type {EntitySelectorPopup} * */
         const selectorPopup = window.$components.first('entity-selector-popup');
         selectorPopup.show(entity => {
             this.setValue(entity.id, entity.name);
@@ -44,7 +48,7 @@ export class PagePicker extends Component {
         toggleElem(this.defaultDisplay, !hasValue);
         toggleElem(this.display, hasValue);
         if (hasValue) {
-            let id = this.getAssetIdFromVal();
+            const id = this.getAssetIdFromVal();
             this.display.textContent = `#${id}, ${name}`;
             this.display.href = window.baseUrl(`/link/${id}`);
         }
@@ -55,7 +59,3 @@ export class PagePicker extends Component {
     }
 
 }
-
-function toggleElem(elem, show) {
-    elem.style.display = show ? null : 'none';
-}
\ No newline at end of file
index 58ead1d57620b58a798ccdc0dfc3708121c8a1f9..800403c61a76e3b36bc46da922c15f264be65d3d 100644 (file)
@@ -1,4 +1,4 @@
-import {Component} from "./component";
+import {Component} from './component';
 
 export class PermissionsTable extends Component {
 
@@ -41,7 +41,7 @@ export class PermissionsTable extends Component {
         const tableRows = this.container.querySelectorAll(this.rowSelector);
         const inputsToToggle = [];
 
-        for (let row of tableRows) {
+        for (const row of tableRows) {
             const targetCell = row.children[colIndex];
             if (targetCell) {
                 inputsToToggle.push(...targetCell.querySelectorAll('input[type=checkbox]'));
@@ -57,10 +57,10 @@ export class PermissionsTable extends Component {
 
     toggleAllInputs(inputsToToggle) {
         const currentState = inputsToToggle.length > 0 ? inputsToToggle[0].checked : false;
-        for (let checkbox of inputsToToggle) {
+        for (const checkbox of inputsToToggle) {
             checkbox.checked = !currentState;
             checkbox.dispatchEvent(new Event('change'));
         }
     }
 
-}
\ No newline at end of file
+}
index d884dc7214ce3ac71673ac0bddf71f39c2755fee..e2e2ceca729fd0d5952ece4c3b6eb09bd1657267 100644 (file)
@@ -1,12 +1,13 @@
-import * as DOM from "../services/dom";
-import Clipboard from "clipboard/dist/clipboard.min";
-import {Component} from "./component";
-
+import * as DOM from '../services/dom';
+import {Component} from './component';
+import {copyTextToClipboard} from '../services/clipboard';
 
 export class Pointer extends Component {
 
     setup() {
         this.container = this.$el;
+        this.input = this.$refs.input;
+        this.button = this.$refs.button;
         this.pageId = this.$opts.pageId;
 
         // Instance variables
@@ -16,15 +17,17 @@ export class Pointer extends Component {
         this.pointerSectionId = '';
 
         this.setupListeners();
-
-        // Set up clipboard
-        new Clipboard(this.container.querySelector('button'));
     }
 
     setupListeners() {
+        // Copy on copy button click
+        this.button.addEventListener('click', () => {
+            copyTextToClipboard(this.input.value);
+        });
+
         // Select all contents on input click
-        DOM.onChildEvent(this.container, 'input', 'click', (event, input) => {
-            input.select();
+        this.input.addEventListener('click', event => {
+            this.input.select();
             event.stopPropagation();
         });
 
@@ -43,7 +46,7 @@ export class Pointer extends Component {
         });
 
         // Hide pointer when clicking away
-        DOM.onEvents(document.body, ['click', 'focus'], event => {
+        DOM.onEvents(document.body, ['click', 'focus'], () => {
             if (!this.showing || this.isSelection) return;
             this.hidePointer();
         });
@@ -109,15 +112,15 @@ export class Pointer extends Component {
     updateForTarget(element) {
         let inputText = this.pointerModeLink ? window.baseUrl(`/link/${this.pageId}#${this.pointerSectionId}`) : `{{@${this.pageId}#${this.pointerSectionId}}}`;
         if (this.pointerModeLink && !inputText.startsWith('http')) {
-            inputText = window.location.protocol + "//" + window.location.host + inputText;
+            inputText = `${window.location.protocol}//${window.location.host}${inputText}`;
         }
 
-        this.container.querySelector('input').value = inputText;
+        this.input.value = inputText;
 
         // Update anchor if present
         const editAnchor = this.container.querySelector('#pointer-edit');
         if (editAnchor && element) {
-            const editHref = editAnchor.dataset.editHref;
+            const {editHref} = editAnchor.dataset;
             const elementId = element.id;
 
             // get the first 50 characters.
@@ -125,4 +128,5 @@ export class Pointer extends Component {
             editAnchor.href = `${editHref}?content-id=${elementId}&content-text=${encodeURIComponent(queryContent)}`;
         }
     }
-}
\ No newline at end of file
+
+}
index 4c20876f854dbfd8441b7cd4d3d8ffb38bc6c28d..6627365483e69f6c90b37d5eac7528f1f0c71be3 100644 (file)
@@ -1,6 +1,6 @@
-import {fadeIn, fadeOut} from "../services/animations";
-import {onSelect} from "../services/dom";
-import {Component} from "./component";
+import {fadeIn, fadeOut} from '../services/animations';
+import {onSelect} from '../services/dom';
+import {Component} from './component';
 
 /**
  * Popup window that will contain other content.
@@ -26,11 +26,11 @@ export class Popup extends Component {
 
         this.container.addEventListener('click', event => {
             if (event.target === this.container && lastMouseDownTarget === this.container) {
-                return this.hide();
+                this.hide();
             }
         });
 
-        onSelect(this.hideButtons, e => this.hide());
+        onSelect(this.hideButtons, () => this.hide());
     }
 
     hide(onComplete = null) {
@@ -47,7 +47,7 @@ export class Popup extends Component {
     show(onComplete = null, onHide = null) {
         fadeIn(this.container, 120, onComplete);
 
-        this.onkeyup = (event) => {
+        this.onkeyup = event => {
             if (event.key === 'Escape') {
                 this.hide();
             }
@@ -56,4 +56,4 @@ export class Popup extends Component {
         this.onHide = onHide;
     }
 
-}
\ No newline at end of file
+}
index 71b14badc137c3c81a1cc00b1faf1d6038f6b718..add2d0cdac33c433570eab64da7efceff52a53c4 100644 (file)
@@ -1,4 +1,4 @@
-import {Component} from "./component";
+import {Component} from './component';
 
 export class SettingAppColorScheme extends Component {
 
@@ -14,7 +14,7 @@ export class SettingAppColorScheme extends Component {
             this.handleModeChange(newMode);
         });
 
-        const onInputChange = (event) => {
+        const onInputChange = event => {
             this.updateAppColorsFromInputs();
 
             if (event.target.name.startsWith('setting-app-color')) {
@@ -44,7 +44,7 @@ export class SettingAppColorScheme extends Component {
                 cssId = 'primary';
             }
 
-            const varName = '--color-' + cssId;
+            const varName = `--color-${cssId}`;
             document.body.style.setProperty(varName, input.value);
         }
     }
@@ -57,9 +57,8 @@ export class SettingAppColorScheme extends Component {
         const lightName = input.name.replace('-color', '-color-light');
         const hexVal = input.value;
         const rgb = this.hexToRgb(hexVal);
-        const rgbLightVal = 'rgba('+ [rgb.r, rgb.g, rgb.b, '0.15'].join(',') +')';
+        const rgbLightVal = `rgba(${[rgb.r, rgb.g, rgb.b, '0.15'].join(',')})`;
 
-        console.log(input.name, lightName, hexVal, rgbLightVal)
         const lightColorInput = this.container.querySelector(`input[name="${lightName}"][type="hidden"]`);
         lightColorInput.value = rgbLightVal;
     }
@@ -75,7 +74,7 @@ export class SettingAppColorScheme extends Component {
         return {
             r: result ? parseInt(result[1], 16) : 0,
             g: result ? parseInt(result[2], 16) : 0,
-            b: result ? parseInt(result[3], 16) : 0
+            b: result ? parseInt(result[3], 16) : 0,
         };
     }
 
index bfb2c93cee4810d290ed2dfdb16303148082b048..bc47b96c0f9ff469962f7a65c2ea8f34060adc3b 100644 (file)
@@ -1,4 +1,4 @@
-import {Component} from "./component";
+import {Component} from './component';
 
 export class SettingColorPicker extends Component {
 
@@ -17,4 +17,5 @@ export class SettingColorPicker extends Component {
         this.colorInput.value = value;
         this.colorInput.dispatchEvent(new Event('change', {bubbles: true}));
     }
-}
\ No newline at end of file
+
+}
index 992be9f826dad07c36b74abcdbc647ff53dc0ce2..6563ced0d725a47591fb55e1e1288e5d358383f4 100644 (file)
@@ -1,4 +1,4 @@
-import {Component} from "./component";
+import {Component} from './component';
 
 export class SettingHomepageControl extends Component {
 
@@ -14,4 +14,5 @@ export class SettingHomepageControl extends Component {
         const showPagePicker = this.typeControl.value === 'page';
         this.pagePickerContainer.style.display = (showPagePicker ? 'block' : 'none');
     }
-}
\ No newline at end of file
+
+}
index e4aefc5918bb41d0bcfb6be918263692afa62b19..01ca11a333f10289f1fd185dbce66bc3a704316b 100644 (file)
@@ -1,17 +1,17 @@
-import Sortable from "sortablejs";
-import {Component} from "./component";
+import Sortable from 'sortablejs';
+import {Component} from './component';
 
 /**
  * @type {Object<string, function(HTMLElement, HTMLElement, HTMLElement)>}
  */
 const itemActions = {
-    move_up(item, shelfBooksList, allBooksList) {
+    move_up(item) {
         const list = item.parentNode;
         const index = Array.from(list.children).indexOf(item);
         const newIndex = Math.max(index - 1, 0);
         list.insertBefore(item, list.children[newIndex] || null);
     },
-    move_down(item, shelfBooksList, allBooksList) {
+    move_down(item) {
         const list = item.parentNode;
         const index = Array.from(list.children).indexOf(item);
         const newIndex = Math.min(index + 2, list.children.length);
@@ -20,7 +20,7 @@ const itemActions = {
     remove(item, shelfBooksList, allBooksList) {
         allBooksList.appendChild(item);
     },
-    add(item, shelfBooksList, allBooksList) {
+    add(item, shelfBooksList) {
         shelfBooksList.appendChild(item);
     },
 };
@@ -62,11 +62,11 @@ export class ShelfSort extends Component {
             }
         });
 
-        this.bookSearchInput.addEventListener('input', event => {
+        this.bookSearchInput.addEventListener('input', () => {
             this.filterBooksByName(this.bookSearchInput.value);
         });
 
-        this.sortButtonContainer.addEventListener('click' , event => {
+        this.sortButtonContainer.addEventListener('click', event => {
             const button = event.target.closest('button[data-sort]');
             if (button) {
                 this.sortShelfBooks(button.dataset.sort);
@@ -78,11 +78,10 @@ export class ShelfSort extends Component {
      * @param {String} filterVal
      */
     filterBooksByName(filterVal) {
-
         // Set height on first search, if not already set, to prevent the distraction
         // of the list height jumping around
         if (!this.allBookList.style.height) {
-            this.allBookList.style.height = this.allBookList.getBoundingClientRect().height + 'px';
+            this.allBookList.style.height = `${this.allBookList.getBoundingClientRect().height}px`;
         }
 
         const books = this.allBookList.children;
@@ -100,7 +99,7 @@ export class ShelfSort extends Component {
      */
     sortItemActionClick(sortItemAction) {
         const sortItem = sortItemAction.closest('.scroll-box-item');
-        const action = sortItemAction.dataset.action;
+        const {action} = sortItemAction.dataset;
 
         const actionFunction = itemActions[action];
         actionFunction(sortItem, this.shelfBookList, this.allBookList);
@@ -122,10 +121,10 @@ export class ShelfSort extends Component {
             const bProp = bookB.dataset[sortProperty].toLowerCase();
 
             if (reverse) {
-                return aProp < bProp ? (aProp === bProp ? 0 : 1) : -1;
+                return bProp.localeCompare(aProp);
             }
 
-            return aProp < bProp ? (aProp === bProp ? 0 : -1) : 1;
+            return aProp.localeCompare(bProp);
         });
 
         for (const book of books) {
@@ -136,4 +135,4 @@ export class ShelfSort extends Component {
         this.onChange();
     }
 
-}
\ No newline at end of file
+}
index 2a2aaa225a518c34413df94de2851745129dfa9e..17e05fc8d140ae7fcd9e22b1310a80459fdfff1c 100644 (file)
@@ -1,4 +1,4 @@
-import {Component} from "./component";
+import {Component} from './component';
 
 /**
  * Keys to ignore when recording shortcuts.
@@ -18,16 +18,16 @@ export class ShortcutInput extends Component {
         this.listenerRecordKey = this.listenerRecordKey.bind(this);
 
         this.input.addEventListener('focus', () => {
-             this.startListeningForInput();
+            this.startListeningForInput();
         });
 
         this.input.addEventListener('blur', () => {
             this.stopListeningForInput();
-        })
+        });
     }
 
     startListeningForInput() {
-        this.input.addEventListener('keydown', this.listenerRecordKey)
+        this.input.addEventListener('keydown', this.listenerRecordKey);
     }
 
     /**
@@ -51,4 +51,4 @@ export class ShortcutInput extends Component {
         this.input.removeEventListener('keydown', this.listenerRecordKey);
     }
 
-}
\ No newline at end of file
+}
index a87213b2e8968070b5a0934cc3db0e6a1ef75fa0..8e927e34cfcd05d2c371eca42ce1c1585fd84396 100644 (file)
@@ -1,4 +1,4 @@
-import {Component} from "./component";
+import {Component} from './component';
 
 function reverseMap(map) {
     const reversed = {};
@@ -8,7 +8,6 @@ function reverseMap(map) {
     return reversed;
 }
 
-
 export class Shortcuts extends Component {
 
     setup() {
@@ -25,7 +24,6 @@ export class Shortcuts extends Component {
 
     setupListeners() {
         window.addEventListener('keydown', event => {
-
             if (event.target.closest('input, select, textarea')) {
                 return;
             }
@@ -35,7 +33,8 @@ export class Shortcuts extends Component {
 
         window.addEventListener('keydown', event => {
             if (event.key === '?') {
-                this.hintsShowing ? this.hideHints() : this.showHints();
+                const action = this.hintsShowing ? this.hideHints : this.showHints;
+                action();
             }
         });
     }
@@ -44,7 +43,6 @@ export class Shortcuts extends Component {
      * @param {KeyboardEvent} event
      */
     handleShortcutPress(event) {
-
         const keys = [
             event.ctrlKey ? 'Ctrl' : '',
             event.metaKey ? 'Cmd' : '',
@@ -90,7 +88,7 @@ export class Shortcuts extends Component {
             return true;
         }
 
-        console.error(`Shortcut attempted to be ran for element type that does not have handling setup`, el);
+        console.error('Shortcut attempted to be ran for element type that does not have handling setup', el);
 
         return false;
     }
@@ -135,10 +133,10 @@ export class Shortcuts extends Component {
 
         const linkage = document.createElement('div');
         linkage.classList.add('shortcut-linkage');
-        linkage.style.left = targetBounds.x + 'px';
-        linkage.style.top = targetBounds.y + 'px';
-        linkage.style.width = targetBounds.width + 'px';
-        linkage.style.height = targetBounds.height + 'px';
+        linkage.style.left = `${targetBounds.x}px`;
+        linkage.style.top = `${targetBounds.y}px`;
+        linkage.style.width = `${targetBounds.width}px`;
+        linkage.style.height = `${targetBounds.height}px`;
 
         wrapper.append(label, linkage);
 
@@ -159,4 +157,5 @@ export class Shortcuts extends Component {
 
         this.hintsShowing = false;
     }
-}
\ No newline at end of file
+
+}
index bbbd92088ab9f3191e516cecae9833c2058b05a7..7b0c4f243b487ff36853f0cb73985d1be78e74ed 100644 (file)
@@ -1,5 +1,5 @@
-import Sortable from "sortablejs";
-import {Component} from "./component";
+import Sortable from 'sortablejs';
+import {Component} from './component';
 
 /**
  * SortableList
@@ -9,6 +9,7 @@ import {Component} from "./component";
  * the data to set on the data-transfer.
  */
 export class SortableList extends Component {
+
     setup() {
         this.container = this.$el;
         this.handleSelector = this.$opts.handleSelector;
@@ -33,4 +34,5 @@ export class SortableList extends Component {
             dragoverBubble: false,
         });
     }
-}
\ No newline at end of file
+
+}
index da4ac699608f21a11a5392dfd272a3492ae178a9..52faa1d10d46d2b1fe9701b68fd779b603ba9667 100644 (file)
@@ -1,4 +1,4 @@
-import {Component} from "./component";
+import {Component} from './component';
 
 /**
  * Submit on change
@@ -9,8 +9,7 @@ export class SubmitOnChange extends Component {
     setup() {
         this.filter = this.$opts.filter;
 
-        this.$el.addEventListener('change', (event) => {
-
+        this.$el.addEventListener('change', event => {
             if (this.filter && !event.target.matches(this.filter)) {
                 return;
             }
@@ -22,4 +21,4 @@ export class SubmitOnChange extends Component {
         });
     }
 
-}
\ No newline at end of file
+}
index 8d22e3e9b57279648ce4c2bc45856ff351e84a79..560dc6273e3937eeb0744c3b8ae9210eb9bb6d92 100644 (file)
@@ -1,4 +1,4 @@
-import {Component} from "./component";
+import {Component} from './component';
 
 /**
  * Tabs
@@ -46,4 +46,4 @@ export class Tabs extends Component {
         this.$emit('change', {showing: sectionId});
     }
 
-}
\ No newline at end of file
+}
index 24d34055327d506ce6728a28b384ab5f59ac2dce..1172e7775aae0e68d04625236d7254bbd941dda1 100644 (file)
@@ -1,6 +1,7 @@
-import {Component} from "./component";
+import {Component} from './component';
 
 export class TagManager extends Component {
+
     setup() {
         this.addRemoveComponentEl = this.$refs.addRemove;
         this.container = this.$el;
@@ -11,8 +12,7 @@ export class TagManager extends Component {
 
     setupListeners() {
         this.container.addEventListener('input', event => {
-
-            /** @var {AddRemoveRows} **/
+            /** @var {AddRemoveRows} * */
             const addRemoveComponent = window.$components.firstOnElement(this.addRemoveComponentEl, 'add-remove-rows');
             if (!this.hasEmptyRows() && event.target.value) {
                 addRemoveComponent.add();
@@ -22,9 +22,8 @@ export class TagManager extends Component {
 
     hasEmptyRows() {
         const rows = this.container.querySelectorAll(this.rowSelector);
-        const firstEmpty = [...rows].find(row => {
-            return [...row.querySelectorAll('input')].filter(input => input.value).length === 0;
-        });
+        const firstEmpty = [...rows].find(row => [...row.querySelectorAll('input')].filter(input => input.value).length === 0);
         return firstEmpty !== undefined;
     }
-}
\ No newline at end of file
+
+}
index 774336471f00c954ec74b6625566d886d2a1ac7b..56ec876d48bbb66a78e65038e4bfdd45a09759d5 100644 (file)
@@ -1,5 +1,5 @@
-import * as DOM from "../services/dom";
-import {Component} from "./component";
+import * as DOM from '../services/dom';
+import {Component} from './component';
 
 export class TemplateManager extends Component {
 
@@ -36,10 +36,10 @@ export class TemplateManager extends Component {
         });
 
         // Search submit button press
-        this.searchButton.addEventListener('click', event => this.performSearch());
+        this.searchButton.addEventListener('click', () => this.performSearch());
 
         // Search cancel button press
-        this.searchCancel.addEventListener('click', event => {
+        this.searchCancel.addEventListener('click', () => {
             this.searchInput.value = '';
             this.performSearch();
         });
@@ -66,7 +66,7 @@ export class TemplateManager extends Component {
 
     async insertTemplate(templateId, action = 'replace') {
         const resp = await window.$http.get(`/templates/${templateId}`);
-        const eventName = 'editor::' + action;
+        const eventName = `editor::${action}`;
         window.$events.emit(eventName, resp.data);
     }
 
@@ -79,10 +79,11 @@ export class TemplateManager extends Component {
 
     async performSearch() {
         const searchTerm = this.searchInput.value;
-        const resp = await window.$http.get(`/templates`, {
-            search: searchTerm
+        const resp = await window.$http.get('/templates', {
+            search: searchTerm,
         });
         this.searchCancel.style.display = searchTerm ? 'block' : 'none';
         this.list.innerHTML = resp.data;
     }
-}
\ No newline at end of file
+
+}
index b749eb54132492efa06b53a05a151fe35c282c3c..e8209eb279794d8a4138acbc68c3c5961ef6828e 100644 (file)
@@ -1,4 +1,4 @@
-import {Component} from "./component";
+import {Component} from './component';
 
 export class ToggleSwitch extends Component {
 
@@ -18,4 +18,4 @@ export class ToggleSwitch extends Component {
         this.input.dispatchEvent(changeEvent);
     }
 
-}
\ No newline at end of file
+}
index ead2ac3d0dae85d4f865cd191d6ff768307eaa76..8ccefb06c482b09da5c7c0d4103f3d8307b8ee12 100644 (file)
@@ -1,4 +1,4 @@
-import {Component} from "./component";
+import {Component} from './component';
 
 export class TriLayout extends Component {
 
@@ -9,8 +9,8 @@ export class TriLayout extends Component {
         this.lastLayoutType = 'none';
         this.onDestroy = null;
         this.scrollCache = {
-            'content': 0,
-            'info': 0,
+            content: 0,
+            info: 0,
         };
         this.lastTabShown = 'content';
 
@@ -19,15 +19,15 @@ export class TriLayout extends Component {
 
         // Watch layout changes
         this.updateLayout();
-        window.addEventListener('resize', event => {
+        window.addEventListener('resize', () => {
             this.updateLayout();
         }, {passive: true});
     }
 
     updateLayout() {
         let newLayout = 'tablet';
-        if (window.innerWidth <= 1000) newLayout =  'mobile';
-        if (window.innerWidth >= 1400) newLayout =  'desktop';
+        if (window.innerWidth <= 1000) newLayout = 'mobile';
+        if (window.innerWidth >= 1400) newLayout = 'desktop';
         if (newLayout === this.lastLayoutType) return;
 
         if (this.onDestroy) {
@@ -53,20 +53,19 @@ export class TriLayout extends Component {
             for (const tab of this.tabs) {
                 tab.removeEventListener('click', this.mobileTabClick);
             }
-        }
+        };
     }
 
     setupDesktop() {
         //
     }
 
-
     /**
      * Action to run when the mobile info toggle bar is clicked/tapped
      * @param event
      */
     mobileTabClick(event) {
-        const tab = event.target.dataset.tab;
+        const {tab} = event.target.dataset;
         this.showTab(tab);
     }
 
@@ -109,4 +108,4 @@ export class TriLayout extends Component {
         this.lastTabShown = tabName;
     }
 
-}
\ No newline at end of file
+}
index d4d88a633c115ab06a9bd718e32ca41090efc109..e6adc3c23c82d4e163c12c4f4c42adfc1d80d249 100644 (file)
@@ -1,5 +1,5 @@
-import {onChildEvent} from "../services/dom";
-import {Component} from "./component";
+import {onChildEvent} from '../services/dom';
+import {Component} from './component';
 
 export class UserSelect extends Component {
 
@@ -20,9 +20,9 @@ export class UserSelect extends Component {
     }
 
     hide() {
-        /** @var {Dropdown} **/
+        /** @var {Dropdown} * */
         const dropdown = window.$components.firstOnElement(this.container, 'dropdown');
         dropdown.hide();
     }
 
-}
\ No newline at end of file
+}
index ad8c59ac2abd7c73a1857602f1f465b754f016e9..68661972d3255a362de6796d48d11d14a6e8452b 100644 (file)
@@ -2,7 +2,7 @@
  * Webhook Events
  * Manages dynamic selection control in the webhook form interface.
  */
-import {Component} from "./component";
+import {Component} from './component';
 
 export class WebhookEvents extends Component {
 
@@ -27,4 +27,4 @@ export class WebhookEvents extends Component {
         }
     }
 
-}
\ No newline at end of file
+}
index 96731a0d9088d6b0009876140874a55768710ea5..21db207e6705451daf45d7a241210b63c1aa00be 100644 (file)
@@ -1,5 +1,5 @@
-import {build as buildEditorConfig} from "../wysiwyg/config";
-import {Component} from "./component";
+import {build as buildEditorConfig} from '../wysiwyg/config';
+import {Component} from './component';
 
 export class WysiwygEditor extends Component {
 
@@ -45,8 +45,8 @@ export class WysiwygEditor extends Component {
      */
     getContent() {
         return {
-            html: this.editor.getContent()
+            html: this.editor.getContent(),
         };
     }
 
-}
\ No newline at end of file
+}
index 9faf43de34143a69933e2aeeea5a2dbb333e2fb4..514bff87d86b87379a49a75035958f637394bd39 100644 (file)
@@ -1,6 +1,7 @@
-import DrawIO from "../services/drawio";
+import * as DrawIO from '../services/drawio';
 
 export class Actions {
+
     /**
      * @param {MarkdownEditor} editor
      */
@@ -13,7 +14,7 @@ export class Actions {
     }
 
     updateAndRender() {
-        const content = this.editor.cm.getValue();
+        const content = this.#getText();
         this.editor.config.inputEl.value = content;
 
         const html = this.editor.markdown.render(content);
@@ -28,50 +29,49 @@ export class Actions {
         return this.lastContent;
     }
 
-    insertImage() {
-        const cursorPos = this.editor.cm.getCursor('from');
-        /** @type {ImageManager} **/
+    showImageInsert() {
+        /** @type {ImageManager} * */
         const imageManager = window.$components.first('image-manager');
+
         imageManager.show(image => {
             const imageUrl = image.thumbs.display || image.url;
-            let selectedText = this.editor.cm.getSelection();
-            let newText = "[![" + (selectedText || image.name) + "](" + imageUrl + ")](" + image.url + ")";
-            this.editor.cm.focus();
-            this.editor.cm.replaceSelection(newText);
-            this.editor.cm.setCursor(cursorPos.line, cursorPos.ch + newText.length);
+            const selectedText = this.#getSelectionText();
+            const newText = `[![${selectedText || image.name}](${imageUrl})](${image.url})`;
+            this.#replaceSelection(newText, newText.length);
         }, 'gallery');
     }
 
+    insertImage() {
+        const newText = `![${this.#getSelectionText()}](http://)`;
+        this.#replaceSelection(newText, newText.length - 1);
+    }
+
     insertLink() {
-        const cursorPos = this.editor.cm.getCursor('from');
-        const selectedText = this.editor.cm.getSelection() || '';
+        const selectedText = this.#getSelectionText();
         const newText = `[${selectedText}]()`;
-        this.editor.cm.focus();
-        this.editor.cm.replaceSelection(newText);
         const cursorPosDiff = (selectedText === '') ? -3 : -1;
-        this.editor.cm.setCursor(cursorPos.line, cursorPos.ch + newText.length+cursorPosDiff);
+        this.#replaceSelection(newText, newText.length + cursorPosDiff);
     }
 
     showImageManager() {
-        const cursorPos = this.editor.cm.getCursor('from');
-        /** @type {ImageManager} **/
+        const selectionRange = this.#getSelectionRange();
+        /** @type {ImageManager} * */
         const imageManager = window.$components.first('image-manager');
         imageManager.show(image => {
-            this.insertDrawing(image, cursorPos);
+            this.#insertDrawing(image, selectionRange);
         }, 'drawio');
     }
 
     // Show the popup link selector and insert a link when finished
     showLinkSelector() {
-        const cursorPos = this.editor.cm.getCursor('from');
-        /** @type {EntitySelectorPopup} **/
+        const selectionRange = this.#getSelectionRange();
+
+        /** @type {EntitySelectorPopup} * */
         const selector = window.$components.first('entity-selector-popup');
         selector.show(entity => {
-            let selectedText = this.editor.cm.getSelection() || entity.name;
-            let newText = `[${selectedText}](${entity.link})`;
-            this.editor.cm.focus();
-            this.editor.cm.replaceSelection(newText);
-            this.editor.cm.setCursor(cursorPos.line, cursorPos.ch + newText.length);
+            const selectedText = this.#getSelectionText(selectionRange) || entity.name;
+            const newText = `[${selectedText}](${entity.link})`;
+            this.#replaceSelection(newText, newText.length, selectionRange);
         });
     }
 
@@ -80,19 +80,16 @@ export class Actions {
         const url = this.editor.config.drawioUrl;
         if (!url) return;
 
-        const cursorPos = this.editor.cm.getCursor('from');
-
-        DrawIO.show(url,() => {
-            return Promise.resolve('');
-        }, (pngData) => {
+        const selectionRange = this.#getSelectionRange();
 
+        DrawIO.show(url, () => Promise.resolve(''), pngData => {
             const data = {
                 image: pngData,
                 uploaded_to: Number(this.editor.config.pageId),
             };
 
-            window.$http.post("/images/drawio", data).then(resp => {
-                this.insertDrawing(resp.data, cursorPos);
+            window.$http.post('/images/drawio', data).then(resp => {
+                this.#insertDrawing(resp.data, selectionRange);
                 DrawIO.close();
             }).catch(err => {
                 this.handleDrawingUploadError(err);
@@ -100,43 +97,36 @@ export class Actions {
         });
     }
 
-    insertDrawing(image, originalCursor) {
+    #insertDrawing(image, originalSelectionRange) {
         const newText = `<div drawio-diagram="${image.id}"><img src="${image.url}"></div>`;
-        this.editor.cm.focus();
-        this.editor.cm.replaceSelection(newText);
-        this.editor.cm.setCursor(originalCursor.line, originalCursor.ch + newText.length);
+        this.#replaceSelection(newText, newText.length, originalSelectionRange);
     }
 
     // Show draw.io if enabled and handle save.
     editDrawing(imgContainer) {
-        const drawioUrl = this.editor.config.drawioUrl;
+        const {drawioUrl} = this.editor.config;
         if (!drawioUrl) {
             return;
         }
 
-        const cursorPos = this.editor.cm.getCursor('from');
+        const selectionRange = this.#getSelectionRange();
         const drawingId = imgContainer.getAttribute('drawio-diagram');
 
-        DrawIO.show(drawioUrl, () => {
-            return DrawIO.load(drawingId);
-        }, (pngData) => {
-
+        DrawIO.show(drawioUrl, () => DrawIO.load(drawingId), pngData => {
             const data = {
                 image: pngData,
                 uploaded_to: Number(this.editor.config.pageId),
             };
 
-            window.$http.post("/images/drawio", data).then(resp => {
+            window.$http.post('/images/drawio', data).then(resp => {
                 const newText = `<div drawio-diagram="${resp.data.id}"><img src="${resp.data.url}"></div>`;
-                const newContent = this.editor.cm.getValue().split('\n').map(line => {
+                const newContent = this.#getText().split('\n').map(line => {
                     if (line.indexOf(`drawio-diagram="${drawingId}"`) !== -1) {
                         return newText;
                     }
                     return line;
                 }).join('\n');
-                this.editor.cm.setValue(newContent);
-                this.editor.cm.setCursor(cursorPos);
-                this.editor.cm.focus();
+                this.#setText(newContent, selectionRange);
                 DrawIO.close();
             }).catch(err => {
                 this.handleDrawingUploadError(err);
@@ -150,12 +140,12 @@ export class Actions {
         } else {
             window.$events.emit('error', this.editor.config.text.imageUploadError);
         }
-        console.log(error);
+        console.error(error);
     }
 
     // Make the editor full screen
     fullScreen() {
-        const container = this.editor.config.container;
+        const {container} = this.editor.config;
         const alreadyFullscreen = container.classList.contains('fullscreen');
         container.classList.toggle('fullscreen', !alreadyFullscreen);
         document.body.classList.toggle('markdown-fullscreen', !alreadyFullscreen);
@@ -167,29 +157,30 @@ export class Actions {
             return;
         }
 
-        const content = this.editor.cm.getValue();
-        const lines = content.split(/\r?\n/);
-        let lineNumber = lines.findIndex(line => {
-            return line && line.indexOf(searchText) !== -1;
-        });
+        const text = this.editor.cm.state.doc;
+        let lineCount = 1;
+        let scrollToLine = -1;
+        for (const line of text.iterLines()) {
+            if (line.includes(searchText)) {
+                scrollToLine = lineCount;
+                break;
+            }
+            lineCount += 1;
+        }
 
-        if (lineNumber === -1) {
+        if (scrollToLine === -1) {
             return;
         }
 
-        this.editor.cm.scrollIntoView({
-            line: lineNumber,
-        }, 200);
-        this.editor.cm.focus();
-        // set the cursor location.
-        this.editor.cm.setCursor({
-            line: lineNumber,
-            char: lines[lineNumber].length
-        })
+        const line = text.line(scrollToLine);
+        this.#setSelection(line.from, line.to, true);
+        this.focus();
     }
 
     focus() {
-        this.editor.cm.focus();
+        if (!this.editor.cm.hasFocus) {
+            this.editor.cm.focus();
+        }
     }
 
     /**
@@ -197,7 +188,7 @@ export class Actions {
      * @param {String} content
      */
     insertContent(content) {
-        this.editor.cm.replaceSelection(content);
+        this.#replaceSelection(content, content.length);
     }
 
     /**
@@ -205,11 +196,11 @@ export class Actions {
      * @param {String} content
      */
     prependContent(content) {
-        const cursorPos = this.editor.cm.getCursor('from');
-        const newContent = content + '\n' + this.editor.cm.getValue();
-        this.editor.cm.setValue(newContent);
-        const prependLineCount = content.split('\n').length;
-        this.editor.cm.setCursor(cursorPos.line + prependLineCount, cursorPos.ch);
+        content = this.#cleanTextForEditor(content);
+        const selectionRange = this.#getSelectionRange();
+        const selectFrom = selectionRange.from + content.length + 1;
+        this.#dispatchChange(0, 0, `${content}\n`, selectFrom);
+        this.focus();
     }
 
     /**
@@ -217,10 +208,9 @@ export class Actions {
      * @param {String} content
      */
     appendContent(content) {
-        const cursorPos = this.editor.cm.getCursor('from');
-        const newContent = this.editor.cm.getValue() + '\n' + content;
-        this.editor.cm.setValue(newContent);
-        this.editor.cm.setCursor(cursorPos.line, cursorPos.ch);
+        content = this.#cleanTextForEditor(content);
+        this.#dispatchChange(this.editor.cm.state.doc.length, `\n${content}`);
+        this.focus();
     }
 
     /**
@@ -228,18 +218,7 @@ export class Actions {
      * @param {String} content
      */
     replaceContent(content) {
-        this.editor.cm.setValue(content);
-    }
-
-    /**
-     * @param {String|RegExp} search
-     * @param {String} replace
-     */
-    findAndReplaceContent(search, replace) {
-        const text = this.editor.cm.getValue();
-        const cursor = this.editor.cm.listSelections();
-        this.editor.cm.setValue(text.replace(search, replace));
-        this.editor.cm.setSelections(cursor);
+        this.#setText(content);
     }
 
     /**
@@ -247,51 +226,30 @@ export class Actions {
      * @param {String} newStart
      */
     replaceLineStart(newStart) {
-        const cursor = this.editor.cm.getCursor();
-        let lineContent = this.editor.cm.getLine(cursor.line);
-        const lineLen = lineContent.length;
+        const selectionRange = this.#getSelectionRange();
+        const line = this.editor.cm.state.doc.lineAt(selectionRange.from);
+
+        const lineContent = line.text;
         const lineStart = lineContent.split(' ')[0];
 
         // Remove symbol if already set
         if (lineStart === newStart) {
-            lineContent = lineContent.replace(`${newStart} `, '');
-            this.editor.cm.replaceRange(lineContent, {line: cursor.line, ch: 0}, {line: cursor.line, ch: lineLen});
-            this.editor.cm.setCursor({line: cursor.line, ch: cursor.ch - (newStart.length + 1)});
+            const newLineContent = lineContent.replace(`${newStart} `, '');
+            const selectFrom = selectionRange.from + (newLineContent.length - lineContent.length);
+            this.#dispatchChange(line.from, line.to, newLineContent, selectFrom);
             return;
         }
 
+        let newLineContent = lineContent;
         const alreadySymbol = /^[#>`]/.test(lineStart);
-        let posDif = 0;
         if (alreadySymbol) {
-            posDif = newStart.length - lineStart.length;
-            lineContent = lineContent.replace(lineStart, newStart).trim();
+            newLineContent = lineContent.replace(lineStart, newStart).trim();
         } else if (newStart !== '') {
-            posDif = newStart.length + 1;
-            lineContent = newStart + ' ' + lineContent;
+            newLineContent = `${newStart} ${lineContent}`;
         }
-        this.editor.cm.replaceRange(lineContent, {line: cursor.line, ch: 0}, {line: cursor.line, ch: lineLen});
-        this.editor.cm.setCursor({line: cursor.line, ch: cursor.ch + posDif});
-    }
 
-    /**
-     * Wrap the line in the given start and end contents.
-     * @param {String} start
-     * @param {String} end
-     */
-    wrapLine(start, end) {
-        const cursor = this.editor.cm.getCursor();
-        const lineContent = this.editor.cm.getLine(cursor.line);
-        const lineLen = lineContent.length;
-        let newLineContent = lineContent;
-
-        if (lineContent.indexOf(start) === 0 && lineContent.slice(-end.length) === end) {
-            newLineContent = lineContent.slice(start.length, lineContent.length - end.length);
-        } else {
-            newLineContent = `${start}${lineContent}${end}`;
-        }
-
-        this.editor.cm.replaceRange(newLineContent, {line: cursor.line, ch: 0}, {line: cursor.line, ch: lineLen});
-        this.editor.cm.setCursor({line: cursor.line, ch: cursor.ch + start.length});
+        const selectFrom = selectionRange.from + (newLineContent.length - lineContent.length);
+        this.#dispatchChange(line.from, line.to, newLineContent, selectFrom);
     }
 
     /**
@@ -300,37 +258,43 @@ export class Actions {
      * @param {String} end
      */
     wrapSelection(start, end) {
-        const selection = this.editor.cm.getSelection();
-        if (selection === '') return this.wrapLine(start, end);
+        const selectRange = this.#getSelectionRange();
+        const selectionText = this.#getSelectionText(selectRange);
+        if (!selectionText) {
+            this.#wrapLine(start, end);
+            return;
+        }
 
-        let newSelection = selection;
-        const frontDiff = 0;
-        let endDiff;
+        let newSelectionText = selectionText;
+        let newRange;
 
-        if (selection.indexOf(start) === 0 && selection.slice(-end.length) === end) {
-            newSelection = selection.slice(start.length, selection.length - end.length);
-            endDiff = -(end.length + start.length);
+        if (selectionText.startsWith(start) && selectionText.endsWith(end)) {
+            newSelectionText = selectionText.slice(start.length, selectionText.length - end.length);
+            newRange = selectRange.extend(selectRange.from, selectRange.to - (start.length + end.length));
         } else {
-            newSelection = `${start}${selection}${end}`;
-            endDiff = start.length + end.length;
+            newSelectionText = `${start}${selectionText}${end}`;
+            newRange = selectRange.extend(selectRange.from, selectRange.to + (start.length + end.length));
         }
 
-        const selections = this.editor.cm.listSelections()[0];
-        this.editor.cm.replaceSelection(newSelection);
-        const headFirst = selections.head.ch <= selections.anchor.ch;
-        selections.head.ch += headFirst ? frontDiff : endDiff;
-        selections.anchor.ch += headFirst ? endDiff : frontDiff;
-        this.editor.cm.setSelections([selections]);
+        this.#dispatchChange(
+            selectRange.from,
+            selectRange.to,
+            newSelectionText,
+            newRange.anchor,
+            newRange.head,
+        );
     }
 
     replaceLineStartForOrderedList() {
-        const cursor = this.editor.cm.getCursor();
-        const prevLineContent = this.editor.cm.getLine(cursor.line - 1) || '';
-        const listMatch = prevLineContent.match(/^(\s*)(\d)([).])\s/) || [];
+        const selectionRange = this.#getSelectionRange();
+        const line = this.editor.cm.state.doc.lineAt(selectionRange.from);
+        const prevLine = this.editor.cm.state.doc.line(line.number - 1);
+
+        const listMatch = prevLine.text.match(/^(\s*)(\d)([).])\s/) || [];
 
         const number = (Number(listMatch[2]) || 0) + 1;
         const whiteSpace = listMatch[1] || '';
-        const listMark = listMatch[3] || '.'
+        const listMark = listMatch[3] || '.';
 
         const prefix = `${whiteSpace}${number}${listMark}`;
         return this.replaceLineStart(prefix);
@@ -341,118 +305,247 @@ export class Actions {
      * Creates a callout block if none existing, and removes it if cycling past the danger type.
      */
     cycleCalloutTypeAtSelection() {
-        const selectionRange = this.editor.cm.listSelections()[0];
-        const lineContent = this.editor.cm.getLine(selectionRange.anchor.line);
-        const lineLength = lineContent.length;
-        const contentRange = {
-            anchor: {line: selectionRange.anchor.line, ch: 0},
-            head: {line: selectionRange.anchor.line, ch: lineLength},
-        };
+        const selectionRange = this.#getSelectionRange();
+        const line = this.editor.cm.state.doc.lineAt(selectionRange.from);
 
         const formats = ['info', 'success', 'warning', 'danger'];
         const joint = formats.join('|');
         const regex = new RegExp(`class="((${joint})\\s+callout|callout\\s+(${joint}))"`, 'i');
-        const matches = regex.exec(lineContent);
+        const matches = regex.exec(line.text);
         const format = (matches ? (matches[2] || matches[3]) : '').toLowerCase();
 
         if (format === formats[formats.length - 1]) {
-            this.wrapLine(`<p class="callout ${formats[formats.length - 1]}">`, '</p>');
+            this.#wrapLine(`<p class="callout ${formats[formats.length - 1]}">`, '</p>');
         } else if (format === '') {
-            this.wrapLine('<p class="callout info">', '</p>');
+            this.#wrapLine('<p class="callout info">', '</p>');
         } else {
             const newFormatIndex = formats.indexOf(format) + 1;
             const newFormat = formats[newFormatIndex];
-            const newContent = lineContent.replace(matches[0], matches[0].replace(format, newFormat));
-            this.editor.cm.replaceRange(newContent, contentRange.anchor, contentRange.head);
+            const newContent = line.text.replace(matches[0], matches[0].replace(format, newFormat));
+            const lineDiff = newContent.length - line.text.length;
+            this.#dispatchChange(
+                line.from,
+                line.to,
+                newContent,
+                selectionRange.anchor + lineDiff,
+                selectionRange.head + lineDiff,
+            );
+        }
+    }
 
-            const chDiff = newContent.length - lineContent.length;
-            selectionRange.anchor.ch += chDiff;
-            if (selectionRange.anchor !== selectionRange.head) {
-                selectionRange.head.ch += chDiff;
-            }
-            this.editor.cm.setSelection(selectionRange.anchor, selectionRange.head);
+    syncDisplayPosition(event) {
+        // Thanks to http://liuhao.im/english/2015/11/10/the-sync-scroll-of-markdown-editor-in-javascript.html
+        const scrollEl = event.target;
+        const atEnd = Math.abs(scrollEl.scrollHeight - scrollEl.clientHeight - scrollEl.scrollTop) < 1;
+        if (atEnd) {
+            this.editor.display.scrollToIndex(-1);
+            return;
+        }
+
+        const blockInfo = this.editor.cm.lineBlockAtHeight(scrollEl.scrollTop);
+        const range = this.editor.cm.state.sliceDoc(0, blockInfo.from);
+        const parser = new DOMParser();
+        const doc = parser.parseFromString(this.editor.markdown.render(range), 'text/html');
+        const totalLines = doc.documentElement.querySelectorAll('body > *');
+        this.editor.display.scrollToIndex(totalLines.length);
+    }
+
+    /**
+     * Fetch and insert the template of the given ID.
+     * The page-relative position provided can be used to determine insert location if possible.
+     * @param {String} templateId
+     * @param {Number} posX
+     * @param {Number} posY
+     */
+    async insertTemplate(templateId, posX, posY) {
+        const cursorPos = this.editor.cm.posAtCoords({x: posX, y: posY}, false);
+        const {data} = await window.$http.get(`/templates/${templateId}`);
+        const content = data.markdown || data.html;
+        this.#dispatchChange(cursorPos, cursorPos, content, cursorPos);
+    }
+
+    /**
+     * Insert multiple images from the clipboard from an event at the provided
+     * screen coordinates (Typically form a paste event).
+     * @param {File[]} images
+     * @param {Number} posX
+     * @param {Number} posY
+     */
+    insertClipboardImages(images, posX, posY) {
+        const cursorPos = this.editor.cm.posAtCoords({x: posX, y: posY}, false);
+        for (const image of images) {
+            this.uploadImage(image, cursorPos);
         }
     }
 
     /**
      * Handle image upload and add image into markdown content
      * @param {File} file
+     * @param {?Number} position
      */
-    uploadImage(file) {
+    async uploadImage(file, position = null) {
         if (file === null || file.type.indexOf('image') !== 0) return;
         let ext = 'png';
 
+        if (position === null) {
+            position = this.#getSelectionRange().from;
+        }
+
         if (file.name) {
-            let fileNameMatches = file.name.match(/\.(.+)$/);
+            const fileNameMatches = file.name.match(/\.(.+)$/);
             if (fileNameMatches.length > 1) ext = fileNameMatches[1];
         }
 
         // Insert image into markdown
-        const id = "image-" + Math.random().toString(16).slice(2);
+        const id = `image-${Math.random().toString(16).slice(2)}`;
         const placeholderImage = window.baseUrl(`/loading.gif#upload${id}`);
-        const selectedText = this.editor.cm.getSelection();
-        const placeHolderText = `![${selectedText}](${placeholderImage})`;
-        const cursor = this.editor.cm.getCursor();
-        this.editor.cm.replaceSelection(placeHolderText);
-        this.editor.cm.setCursor({line: cursor.line, ch: cursor.ch + selectedText.length + 3});
+        const placeHolderText = `![](${placeholderImage})`;
+        this.#dispatchChange(position, position, placeHolderText, position);
 
-        const remoteFilename = "image-" + Date.now() + "." + ext;
+        const remoteFilename = `image-${Date.now()}.${ext}`;
         const formData = new FormData();
         formData.append('file', file, remoteFilename);
         formData.append('uploaded_to', this.editor.config.pageId);
 
-        window.$http.post('/images/gallery', formData).then(resp => {
-            const newContent = `[![${selectedText}](${resp.data.thumbs.display})](${resp.data.url})`;
-            this.findAndReplaceContent(placeHolderText, newContent);
-        }).catch(err => {
+        try {
+            const {data} = await window.$http.post('/images/gallery', formData);
+            const newContent = `[![](${data.thumbs.display})](${data.url})`;
+            this.#findAndReplaceContent(placeHolderText, newContent);
+        } catch (err) {
             window.$events.emit('error', this.editor.config.text.imageUploadError);
-            this.findAndReplaceContent(placeHolderText, selectedText);
-            console.log(err);
-        });
+            this.#findAndReplaceContent(placeHolderText, '');
+            console.error(err);
+        }
     }
 
-    syncDisplayPosition() {
-        // Thanks to http://liuhao.im/english/2015/11/10/the-sync-scroll-of-markdown-editor-in-javascript.html
-        const scroll = this.editor.cm.getScrollInfo();
-        const atEnd = scroll.top + scroll.clientHeight === scroll.height;
-        if (atEnd) {
-            this.editor.display.scrollToIndex(-1);
-            return;
-        }
+    /**
+     * Get the current text of the editor instance.
+     * @return {string}
+     */
+    #getText() {
+        return this.editor.cm.state.doc.toString();
+    }
 
-        const lineNum = this.editor.cm.lineAtHeight(scroll.top, 'local');
-        const range = this.editor.cm.getRange({line: 0, ch: null}, {line: lineNum, ch: null});
-        const parser = new DOMParser();
-        const doc = parser.parseFromString(this.editor.markdown.render(range), 'text/html');
-        const totalLines = doc.documentElement.querySelectorAll('body > *');
-        this.editor.display.scrollToIndex(totalLines.length);
+    /**
+     * Set the text of the current editor instance.
+     * @param {String} text
+     * @param {?SelectionRange} selectionRange
+     */
+    #setText(text, selectionRange = null) {
+        selectionRange = selectionRange || this.#getSelectionRange();
+        this.#dispatchChange(0, this.editor.cm.state.doc.length, text, selectionRange.from);
+        this.focus();
     }
 
     /**
-     * Fetch and insert the template of the given ID.
-     * The page-relative position provided can be used to determine insert location if possible.
-     * @param {String} templateId
-     * @param {Number} posX
-     * @param {Number} posY
+     * Replace the current selection and focus the editor.
+     * Takes an offset for the cursor, after the change, relative to the start of the provided string.
+     * Can be provided a selection range to use instead of the current selection range.
+     * @param {String} newContent
+     * @param {Number} cursorOffset
+     * @param {?SelectionRange} selectionRange
      */
-    insertTemplate(templateId, posX, posY) {
-        const cursorPos = this.editor.cm.coordsChar({left: posX, top: posY});
-        this.editor.cm.setCursor(cursorPos);
-        window.$http.get(`/templates/${templateId}`).then(resp => {
-            const content = resp.data.markdown || resp.data.html;
-            this.editor.cm.replaceSelection(content);
-        });
+    #replaceSelection(newContent, cursorOffset = 0, selectionRange = null) {
+        selectionRange = selectionRange || this.editor.cm.state.selection.main;
+        const selectFrom = selectionRange.from + cursorOffset;
+        this.#dispatchChange(selectionRange.from, selectionRange.to, newContent, selectFrom);
+        this.focus();
     }
 
     /**
-     * Insert multiple images from the clipboard.
-     * @param {File[]} images
+     * Get the text content of the main current selection.
+     * @param {SelectionRange} selectionRange
+     * @return {string}
      */
-    insertClipboardImages(images) {
-        const cursorPos = this.editor.cm.coordsChar({left: event.pageX, top: event.pageY});
-        this.editor.cm.setCursor(cursorPos);
-        for (const image of images) {
-            this.uploadImage(image);
+    #getSelectionText(selectionRange = null) {
+        selectionRange = selectionRange || this.#getSelectionRange();
+        return this.editor.cm.state.sliceDoc(selectionRange.from, selectionRange.to);
+    }
+
+    /**
+     * Get the range of the current main selection.
+     * @return {SelectionRange}
+     */
+    #getSelectionRange() {
+        return this.editor.cm.state.selection.main;
+    }
+
+    /**
+     * Cleans the given text to work with the editor.
+     * Standardises line endings to what's expected.
+     * @param {String} text
+     * @return {String}
+     */
+    #cleanTextForEditor(text) {
+        return text.replace(/\r\n|\r/g, '\n');
+    }
+
+    /**
+     * Find and replace the first occurrence of [search] with [replace]
+     * @param {String} search
+     * @param {String} replace
+     */
+    #findAndReplaceContent(search, replace) {
+        const newText = this.#getText().replace(search, replace);
+        this.#setText(newText);
+    }
+
+    /**
+     * Wrap the line in the given start and end contents.
+     * @param {String} start
+     * @param {String} end
+     */
+    #wrapLine(start, end) {
+        const selectionRange = this.#getSelectionRange();
+        const line = this.editor.cm.state.doc.lineAt(selectionRange.from);
+        const lineContent = line.text;
+        let newLineContent;
+        let lineOffset = 0;
+
+        if (lineContent.startsWith(start) && lineContent.endsWith(end)) {
+            newLineContent = lineContent.slice(start.length, lineContent.length - end.length);
+            lineOffset = -(start.length);
+        } else {
+            newLineContent = `${start}${lineContent}${end}`;
+            lineOffset = start.length;
+        }
+
+        this.#dispatchChange(line.from, line.to, newLineContent, selectionRange.from + lineOffset);
+    }
+
+    /**
+     * Dispatch changes to the editor.
+     * @param {Number} from
+     * @param {?Number} to
+     * @param {?String} text
+     * @param {?Number} selectFrom
+     * @param {?Number} selectTo
+     */
+    #dispatchChange(from, to = null, text = null, selectFrom = null, selectTo = null) {
+        const tr = {changes: {from, to, insert: text}};
+
+        if (selectFrom) {
+            tr.selection = {anchor: selectFrom};
+            if (selectTo) {
+                tr.selection.head = selectTo;
+            }
         }
+
+        this.editor.cm.dispatch(tr);
+    }
+
+    /**
+     * Set the current selection range.
+     * Optionally will scroll the new range into view.
+     * @param {Number} from
+     * @param {Number} to
+     * @param {Boolean} scrollIntoView
+     */
+    #setSelection(from, to, scrollIntoView = false) {
+        this.editor.cm.dispatch({
+            selection: {anchor: from, head: to},
+            scrollIntoView,
+        });
     }
-}
\ No newline at end of file
+
+}
index 8724a23c89e33a04b8e0b700dd6395eeb9beaad0..9d54c19d754b52b6489bc9aad5bdb7299f4d8542 100644 (file)
@@ -1,6 +1,6 @@
-import {provide as provideShortcuts} from "./shortcuts";
-import {debounce} from "../services/util";
-import Clipboard from "../services/clipboard";
+import {provideKeyBindings} from './shortcuts';
+import {debounce} from '../services/util';
+import {Clipboard} from '../services/clipboard';
 
 /**
  * Initiate the codemirror instance for the markdown editor.
@@ -9,62 +9,67 @@ import Clipboard from "../services/clipboard";
  */
 export async function init(editor) {
     const Code = await window.importVersioned('code');
-    const cm = Code.markdownEditor(editor.config.inputEl);
 
-    // Will force to remain as ltr for now due to issues when HTML is in editor.
-    cm.setOption('direction', 'ltr');
-    // Register shortcuts
-    cm.setOption('extraKeys', provideShortcuts(editor, Code.getMetaKey()));
-
-
-    // Register codemirror events
-
-    // Update data on content change
-    cm.on('change', (instance, changeObj) => editor.actions.updateAndRender());
+    /**
+     * @param {ViewUpdate} v
+     */
+    function onViewUpdate(v) {
+        if (v.docChanged) {
+            editor.actions.updateAndRender();
+        }
+    }
 
-    // Handle scroll to sync display view
     const onScrollDebounced = debounce(editor.actions.syncDisplayPosition.bind(editor.actions), 100, false);
     let syncActive = editor.settings.get('scrollSync');
-    editor.settings.onChange('scrollSync', val => syncActive = val);
-    cm.on('scroll', instance => {
-        if (syncActive) {
-            onScrollDebounced(instance);
-        }
+    editor.settings.onChange('scrollSync', val => {
+        syncActive = val;
     });
 
-    // Handle image paste
-    cm.on('paste', (cm, event) => {
-        const clipboard = new Clipboard(event.clipboardData || event.dataTransfer);
-
-        // Don't handle the event ourselves if no items exist of contains table-looking data
-        if (!clipboard.hasItems() || clipboard.containsTabularData()) {
-            return;
-        }
+    const domEventHandlers = {
+        // Handle scroll to sync display view
+        scroll: event => syncActive && onScrollDebounced(event),
+        // Handle image & content drag n drop
+        drop: event => {
+            const templateId = event.dataTransfer.getData('bookstack/template');
+            if (templateId) {
+                event.preventDefault();
+                editor.actions.insertTemplate(templateId, event.pageX, event.pageY);
+            }
 
-        const images = clipboard.getImages();
-        for (const image of images) {
-            editor.actions.uploadImage(image);
-        }
-    });
+            const clipboard = new Clipboard(event.dataTransfer);
+            const clipboardImages = clipboard.getImages();
+            if (clipboardImages.length > 0) {
+                event.stopPropagation();
+                event.preventDefault();
+                editor.actions.insertClipboardImages(clipboardImages, event.pageX, event.pageY);
+            }
+        },
+        // Handle image paste
+        paste: event => {
+            const clipboard = new Clipboard(event.clipboardData || event.dataTransfer);
 
-    // Handle image & content drag n drop
-    cm.on('drop', (cm, event) => {
+            // Don't handle the event ourselves if no items exist of contains table-looking data
+            if (!clipboard.hasItems() || clipboard.containsTabularData()) {
+                return;
+            }
 
-        const templateId = event.dataTransfer.getData('bookstack/template');
-        if (templateId) {
-            event.preventDefault();
-            editor.actions.insertTemplate(templateId, event.pageX, event.pageY);
-        }
+            const images = clipboard.getImages();
+            for (const image of images) {
+                editor.actions.uploadImage(image);
+            }
+        },
+    };
 
-        const clipboard = new Clipboard(event.dataTransfer);
-        const clipboardImages = clipboard.getImages();
-        if (clipboardImages.length > 0) {
-            event.stopPropagation();
-            event.preventDefault();
-            editor.actions.insertClipboardImages(clipboardImages);
-        }
+    const cm = Code.markdownEditor(
+        editor.config.inputEl,
+        onViewUpdate,
+        domEventHandlers,
+        provideKeyBindings(editor),
+    );
 
-    });
+    // Add editor view to window for easy access/debugging.
+    // Not part of official API/Docs
+    window.mdEditorView = cm;
 
     return cm;
-}
\ No newline at end of file
+}
index 3afd03dd5fd2dd6baa8ce9691421d59f4f00274a..c3d803f7048c579b8c86f1e371e9c2b483927be0 100644 (file)
@@ -6,23 +6,22 @@ function getContentToInsert({html, markdown}) {
  * @param {MarkdownEditor} editor
  */
 export function listen(editor) {
-
-    window.$events.listen('editor::replace', (eventContent) => {
+    window.$events.listen('editor::replace', eventContent => {
         const markdown = getContentToInsert(eventContent);
         editor.actions.replaceContent(markdown);
     });
 
-    window.$events.listen('editor::append', (eventContent) => {
+    window.$events.listen('editor::append', eventContent => {
         const markdown = getContentToInsert(eventContent);
         editor.actions.appendContent(markdown);
     });
 
-    window.$events.listen('editor::prepend', (eventContent) => {
+    window.$events.listen('editor::prepend', eventContent => {
         const markdown = getContentToInsert(eventContent);
         editor.actions.prependContent(markdown);
     });
 
-    window.$events.listen('editor::insert', (eventContent) => {
+    window.$events.listen('editor::insert', eventContent => {
         const markdown = getContentToInsert(eventContent);
         editor.actions.insertContent(markdown);
     });
@@ -30,4 +29,4 @@ export function listen(editor) {
     window.$events.listen('editor::focus', () => {
         editor.actions.focus();
     });
-}
\ No newline at end of file
+}
index 2c78da1899fe75504e4578093ebd684c93c479bd..487df1cabcec4d5a9cf9d0c48d5b412d78ff9ee5 100644 (file)
@@ -1,4 +1,4 @@
-import {patchDomFromHtmlString} from "../services/vdom";
+import {patchDomFromHtmlString} from '../services/vdom';
 
 export class Display {
 
@@ -81,7 +81,7 @@ export class Display {
      * @param {String} html
      */
     patchWithHtml(html) {
-        const body = this.doc.body;
+        const {body} = this.doc;
 
         if (body.children.length === 0) {
             const wrap = document.createElement('div');
@@ -102,8 +102,8 @@ export class Display {
         const elems = this.doc.body?.children[0]?.children;
         if (elems && elems.length <= index) return;
 
-        const topElem = (index === -1) ? elems[elems.length-1] : elems[index];
-        topElem.scrollIntoView({ block: 'start', inline: 'nearest', behavior: 'smooth'});
+        const topElem = (index === -1) ? elems[elems.length - 1] : elems[index];
+        topElem.scrollIntoView({block: 'start', inline: 'nearest', behavior: 'smooth'});
     }
 
-}
\ No newline at end of file
+}
index 1cf4cef2bcf04494f0f617a90a43069326fb5985..46c35c850ac77b5e19277c00df784f6a415546c4 100644 (file)
@@ -1,10 +1,9 @@
-import {Markdown} from "./markdown";
-import {Display} from "./display";
-import {Actions} from "./actions";
-import {Settings} from "./settings";
-import {listen} from "./common-events";
-import {init as initCodemirror} from "./codemirror";
-
+import {Markdown} from './markdown';
+import {Display} from './display';
+import {Actions} from './actions';
+import {Settings} from './settings';
+import {listen} from './common-events';
+import {init as initCodemirror} from './codemirror';
 
 /**
  * Initiate a new markdown editor instance.
@@ -12,7 +11,6 @@ import {init as initCodemirror} from "./codemirror";
  * @returns {Promise<MarkdownEditor>}
  */
 export async function init(config) {
-
     /**
      * @type {MarkdownEditor}
      */
@@ -31,7 +29,6 @@ export async function init(config) {
     return editor;
 }
 
-
 /**
  * @typedef MarkdownEditorConfig
  * @property {String} pageId
@@ -49,6 +46,6 @@ export async function init(config) {
  * @property {Display} display
  * @property {Markdown} markdown
  * @property {Actions} actions
- * @property {CodeMirror} cm
+ * @property {EditorView} cm
  * @property {Settings} settings
- */
\ No newline at end of file
+ */
index ef3a02872eabecc99b7161c6a218e4fcab545527..e63184accaf141a8caa78414f8bcb4cfafa2e7d2 100644 (file)
@@ -1,4 +1,4 @@
-import MarkdownIt from "markdown-it";
+import MarkdownIt from 'markdown-it';
 import mdTasksLists from 'markdown-it-task-lists';
 
 export class Markdown {
@@ -24,7 +24,5 @@ export class Markdown {
     render(markdown) {
         return this.renderer.render(markdown);
     }
-}
-
-
 
+}
index 62aab82e9d5dda088bbcfe53dd26cba8227b1c49..b843aaa8a2b2bed55133a17c075473285db5c595 100644 (file)
@@ -21,7 +21,7 @@ export class Settings {
 
     listenToInputChanges(inputs) {
         for (const input of inputs) {
-            input.addEventListener('change', event => {
+            input.addEventListener('change', () => {
                 const name = input.getAttribute('name').replace('md-', '');
                 this.set(name, input.checked);
             });
@@ -59,4 +59,5 @@ export class Settings {
         listeners.push(callback);
         this.changeListeners[key] = listeners;
     }
-}
\ No newline at end of file
+
+}
index 17ffe2fb3a6fc69d94654fdcc63cc631d0af21f6..543e6dcdde67dd26ab8beb8f2987a71932151fbc 100644 (file)
@@ -1,48 +1,62 @@
 /**
- * Provide shortcuts for the given codemirror instance.
+ * Provide shortcuts for the editor instance.
  * @param {MarkdownEditor} editor
- * @param {String} metaKey
  * @returns {Object<String, Function>}
  */
-export function provide(editor, metaKey) {
+function provide(editor) {
     const shortcuts = {};
 
     // Insert Image shortcut
-    shortcuts[`${metaKey}-Alt-I`] = function(cm) {
-        const selectedText = cm.getSelection();
-        const newText = `![${selectedText}](http://)`;
-        const cursorPos = cm.getCursor('from');
-        cm.replaceSelection(newText);
-        cm.setCursor(cursorPos.line, cursorPos.ch + newText.length -1);
-    };
+    shortcuts['Shift-Mod-i'] = () => editor.actions.insertImage();
 
     // Save draft
-    shortcuts[`${metaKey}-S`] = cm => window.$events.emit('editor-save-draft');
+    shortcuts['Mod-s'] = () => window.$events.emit('editor-save-draft');
 
     // Save page
-    shortcuts[`${metaKey}-Enter`] = cm => window.$events.emit('editor-save-page');
+    shortcuts['Mod-Enter'] = () => window.$events.emit('editor-save-page');
 
     // Show link selector
-    shortcuts[`Shift-${metaKey}-K`] = cm => editor.actions.showLinkSelector();
+    shortcuts['Shift-Mod-k'] = () => editor.actions.showLinkSelector();
 
     // Insert Link
-    shortcuts[`${metaKey}-K`] = cm => editor.actions.insertLink();
+    shortcuts['Mod-k'] = () => editor.actions.insertLink();
 
     // FormatShortcuts
-    shortcuts[`${metaKey}-1`] = cm => editor.actions.replaceLineStart('##');
-    shortcuts[`${metaKey}-2`] = cm => editor.actions.replaceLineStart('###');
-    shortcuts[`${metaKey}-3`] = cm => editor.actions.replaceLineStart('####');
-    shortcuts[`${metaKey}-4`] = cm => editor.actions.replaceLineStart('#####');
-    shortcuts[`${metaKey}-5`] = cm => editor.actions.replaceLineStart('');
-    shortcuts[`${metaKey}-D`] = cm => editor.actions.replaceLineStart('');
-    shortcuts[`${metaKey}-6`] = cm => editor.actions.replaceLineStart('>');
-    shortcuts[`${metaKey}-Q`] = cm => editor.actions.replaceLineStart('>');
-    shortcuts[`${metaKey}-7`] = cm => editor.actions.wrapSelection('\n```\n', '\n```');
-    shortcuts[`${metaKey}-8`] = cm => editor.actions.wrapSelection('`', '`');
-    shortcuts[`Shift-${metaKey}-E`] = cm => editor.actions.wrapSelection('`', '`');
-    shortcuts[`${metaKey}-9`] = cm => editor.actions.cycleCalloutTypeAtSelection();
-    shortcuts[`${metaKey}-P`] = cm => editor.actions.replaceLineStart('-')
-    shortcuts[`${metaKey}-O`] = cm => editor.actions.replaceLineStartForOrderedList()
+    shortcuts['Mod-1'] = () => editor.actions.replaceLineStart('##');
+    shortcuts['Mod-2'] = () => editor.actions.replaceLineStart('###');
+    shortcuts['Mod-3'] = () => editor.actions.replaceLineStart('####');
+    shortcuts['Mod-4'] = () => editor.actions.replaceLineStart('#####');
+    shortcuts['Mod-5'] = () => editor.actions.replaceLineStart('');
+    shortcuts['Mod-d'] = () => editor.actions.replaceLineStart('');
+    shortcuts['Mod-6'] = () => editor.actions.replaceLineStart('>');
+    shortcuts['Mod-q'] = () => editor.actions.replaceLineStart('>');
+    shortcuts['Mod-7'] = () => editor.actions.wrapSelection('\n```\n', '\n```');
+    shortcuts['Mod-8'] = () => editor.actions.wrapSelection('`', '`');
+    shortcuts['Shift-Mod-e'] = () => editor.actions.wrapSelection('`', '`');
+    shortcuts['Mod-9'] = () => editor.actions.cycleCalloutTypeAtSelection();
+    shortcuts['Mod-p'] = () => editor.actions.replaceLineStart('-');
+    shortcuts['Mod-o'] = () => editor.actions.replaceLineStartForOrderedList();
 
     return shortcuts;
-}
\ No newline at end of file
+}
+
+/**
+ * Get the editor shortcuts in CodeMirror keybinding format.
+ * @param {MarkdownEditor} editor
+ * @return {{key: String, run: function, preventDefault: boolean}[]}
+ */
+export function provideKeyBindings(editor) {
+    const shortcuts = provide(editor);
+    const keyBindings = [];
+
+    const wrapAction = action => () => {
+        action();
+        return true;
+    };
+
+    for (const [shortcut, action] of Object.entries(shortcuts)) {
+        keyBindings.push({key: shortcut, run: wrapAction(action), preventDefault: true});
+    }
+
+    return keyBindings;
+}
index 12b8077cf7dccec37dc48e6f83a7c6aa1ce43c86..bc983c8072aa529d7693018716c7b0ed255db087 100644 (file)
@@ -5,6 +5,53 @@
  */
 const animateStylesCleanupMap = new WeakMap();
 
+/**
+ * Animate the css styles of an element using FLIP animation techniques.
+ * Styles must be an object where the keys are style properties, camelcase, and the values
+ * are an array of two items in the format [initialValue, finalValue]
+ * @param {Element} element
+ * @param {Object} styles
+ * @param {Number} animTime
+ * @param {Function} onComplete
+ */
+function animateStyles(element, styles, animTime = 400, onComplete = null) {
+    const styleNames = Object.keys(styles);
+    for (const style of styleNames) {
+        element.style[style] = styles[style][0];
+    }
+
+    const cleanup = () => {
+        for (const style of styleNames) {
+            element.style[style] = null;
+        }
+        element.style.transition = null;
+        element.removeEventListener('transitionend', cleanup);
+        animateStylesCleanupMap.delete(element);
+        if (onComplete) onComplete();
+    };
+
+    setTimeout(() => {
+        element.style.transition = `all ease-in-out ${animTime}ms`;
+        for (const style of styleNames) {
+            element.style[style] = styles[style][1];
+        }
+
+        element.addEventListener('transitionend', cleanup);
+        animateStylesCleanupMap.set(element, cleanup);
+    }, 15);
+}
+
+/**
+ * Run the active cleanup action for the given element.
+ * @param {Element} element
+ */
+function cleanupExistingElementAnimation(element) {
+    if (animateStylesCleanupMap.has(element)) {
+        const oldCleanup = animateStylesCleanupMap.get(element);
+        oldCleanup();
+    }
+}
+
 /**
  * Fade in the given element.
  * @param {Element} element
@@ -15,7 +62,7 @@ export function fadeIn(element, animTime = 400, onComplete = null) {
     cleanupExistingElementAnimation(element);
     element.style.display = 'block';
     animateStyles(element, {
-        opacity: ['0', '1']
+        opacity: ['0', '1'],
     }, animTime, () => {
         if (onComplete) onComplete();
     });
@@ -30,7 +77,7 @@ export function fadeIn(element, animTime = 400, onComplete = null) {
 export function fadeOut(element, animTime = 400, onComplete = null) {
     cleanupExistingElementAnimation(element);
     animateStyles(element, {
-        opacity: ['1', '0']
+        opacity: ['1', '0'],
     }, animTime, () => {
         element.style.display = 'none';
         if (onComplete) onComplete();
@@ -113,50 +160,3 @@ export function transitionHeight(element, animTime = 400) {
         animateStyles(element, animStyles, animTime);
     };
 }
-
-/**
- * Animate the css styles of an element using FLIP animation techniques.
- * Styles must be an object where the keys are style properties, camelcase, and the values
- * are an array of two items in the format [initialValue, finalValue]
- * @param {Element} element
- * @param {Object} styles
- * @param {Number} animTime
- * @param {Function} onComplete
- */
-function animateStyles(element, styles, animTime = 400, onComplete = null) {
-    const styleNames = Object.keys(styles);
-    for (let style of styleNames) {
-        element.style[style] = styles[style][0];
-    }
-
-    const cleanup = () => {
-        for (let style of styleNames) {
-            element.style[style] = null;
-        }
-        element.style.transition = null;
-        element.removeEventListener('transitionend', cleanup);
-        animateStylesCleanupMap.delete(element);
-        if (onComplete) onComplete();
-    };
-
-    setTimeout(() => {
-        element.style.transition = `all ease-in-out ${animTime}ms`;
-        for (let style of styleNames) {
-            element.style[style] = styles[style][1];
-        }
-
-        element.addEventListener('transitionend', cleanup);
-        animateStylesCleanupMap.set(element, cleanup);
-    }, 15);
-}
-
-/**
- * Run the active cleanup action for the given element.
- * @param {Element} element
- */
-function cleanupExistingElementAnimation(element) {
-    if (animateStylesCleanupMap.has(element)) {
-        const oldCleanup = animateStylesCleanupMap.get(element);
-        oldCleanup();
-    }
-}
\ No newline at end of file
index da921e51590d0c67e63b994e4cf1d4a25ae5e772..02db29be00aa98869c9bf1eb17df8a590a8a1370 100644 (file)
@@ -1,5 +1,4 @@
-
-class Clipboard {
+export class Clipboard {
 
     /**
      * Constructor
@@ -21,7 +20,7 @@ class Clipboard {
      * @return {boolean}
      */
     containsTabularData() {
-        const rtfData = this.data.getData( 'text/rtf');
+        const rtfData = this.data.getData('text/rtf');
         return rtfData && rtfData.includes('\\trowd');
     }
 
@@ -30,8 +29,8 @@ class Clipboard {
      * @return {Array<File>}
      */
     getImages() {
-        const types = this.data.types;
-        const files = this.data.files;
+        const {types} = this.data;
+        const {files} = this.data;
         const images = [];
 
         for (const type of types) {
@@ -49,6 +48,21 @@ class Clipboard {
 
         return images;
     }
+
 }
 
-export default Clipboard;
\ No newline at end of file
+export async function copyTextToClipboard(text) {
+    if (window.isSecureContext && navigator.clipboard) {
+        await navigator.clipboard.writeText(text);
+        return;
+    }
+
+    // Backup option where we can't use the navigator.clipboard API
+    const tempInput = document.createElement('textarea');
+    tempInput.style = 'position: absolute; left: -1000px; top: -1000px;';
+    tempInput.value = text;
+    document.body.appendChild(tempInput);
+    tempInput.select();
+    document.execCommand('copy');
+    document.body.removeChild(tempInput);
+}
index d1503db4d4a9f20dc60e6f386e5f53a01c7b9708..beb0ce92fbf38863e91f27e03e8528f7e86cc378 100644 (file)
@@ -1,4 +1,4 @@
-import {kebabToCamel, camelToKebab} from "./text";
+import {kebabToCamel, camelToKebab} from './text';
 
 /**
  * A mapping of active components keyed by name, with values being arrays of component
@@ -19,44 +19,6 @@ const componentModelMap = {};
  */
 const elementComponentMap = new WeakMap();
 
-/**
- * Initialize a component instance on the given dom element.
- * @param {String} name
- * @param {Element} element
- */
-function initComponent(name, element) {
-    /** @type {Function<Component>|undefined} **/
-    const componentModel = componentModelMap[name];
-    if (componentModel === undefined) return;
-
-    // Create our component instance
-    /** @type {Component} **/
-    let instance;
-    try {
-        instance = new componentModel();
-        instance.$name = name;
-        instance.$el = element;
-        const allRefs = parseRefs(name, element);
-        instance.$refs = allRefs.refs;
-        instance.$manyRefs = allRefs.manyRefs;
-        instance.$opts = parseOpts(name, element);
-        instance.setup();
-    } catch (e) {
-        console.error('Failed to create component', e, name, element);
-    }
-
-    // Add to global listing
-    if (typeof components[name] === "undefined") {
-        components[name] = [];
-    }
-    components[name].push(instance);
-
-    // Add to element mapping
-    const elComponents = elementComponentMap.get(element) || {};
-    elComponents[name] = instance;
-    elementComponentMap.set(element, elComponents);
-}
-
 /**
  * Parse out the element references within the given element
  * for the given component name.
@@ -67,7 +29,7 @@ function parseRefs(name, element) {
     const refs = {};
     const manyRefs = {};
 
-    const prefix = `${name}@`
+    const prefix = `${name}@`;
     const selector = `[refs*="${prefix}"]`;
     const refElems = [...element.querySelectorAll(selector)];
     if (element.matches(selector)) {
@@ -93,13 +55,13 @@ function parseRefs(name, element) {
 
 /**
  * Parse out the element component options.
- * @param {String} name
+ * @param {String} componentName
  * @param {Element} element
  * @return {Object<String, String>}
  */
-function parseOpts(name, element) {
+function parseOpts(componentName, element) {
     const opts = {};
-    const prefix = `option:${name}:`;
+    const prefix = `option:${componentName}:`;
     for (const {name, value} of element.attributes) {
         if (name.startsWith(prefix)) {
             const optName = name.replace(prefix, '');
@@ -109,12 +71,50 @@ function parseOpts(name, element) {
     return opts;
 }
 
+/**
+ * Initialize a component instance on the given dom element.
+ * @param {String} name
+ * @param {Element} element
+ */
+function initComponent(name, element) {
+    /** @type {Function<Component>|undefined} * */
+    const ComponentModel = componentModelMap[name];
+    if (ComponentModel === undefined) return;
+
+    // Create our component instance
+    /** @type {Component} * */
+    let instance;
+    try {
+        instance = new ComponentModel();
+        instance.$name = name;
+        instance.$el = element;
+        const allRefs = parseRefs(name, element);
+        instance.$refs = allRefs.refs;
+        instance.$manyRefs = allRefs.manyRefs;
+        instance.$opts = parseOpts(name, element);
+        instance.setup();
+    } catch (e) {
+        console.error('Failed to create component', e, name, element);
+    }
+
+    // Add to global listing
+    if (typeof components[name] === 'undefined') {
+        components[name] = [];
+    }
+    components[name].push(instance);
+
+    // Add to element mapping
+    const elComponents = elementComponentMap.get(element) || {};
+    elComponents[name] = instance;
+    elementComponentMap.set(element, elComponents);
+}
+
 /**
  * Initialize all components found within the given element.
  * @param {Element|Document} parentElement
  */
 export function init(parentElement = document) {
-    const componentElems = parentElement.querySelectorAll(`[component],[components]`);
+    const componentElems = parentElement.querySelectorAll('[component],[components]');
 
     for (const el of componentElems) {
         const componentNames = `${el.getAttribute('component') || ''} ${(el.getAttribute('components'))}`.toLowerCase().split(' ').filter(Boolean);
@@ -162,4 +162,4 @@ export function get(name) {
 export function firstOnElement(element, name) {
     const elComponents = elementComponentMap.get(element) || {};
     return elComponents[name] || null;
-}
\ No newline at end of file
+}
index 119d8fa605850564c0d204188b67e441ac017892..2e6b34aeededad43ba2a57dd8cecea6d4f019d27 100644 (file)
@@ -1,24 +1,23 @@
-
 export function getCurrentDay() {
-    let date = new Date();
-    let month = date.getMonth() + 1;
-    let day = date.getDate();
+    const date = new Date();
+    const month = date.getMonth() + 1;
+    const day = date.getDate();
 
-    return `${date.getFullYear()}-${(month>9?'':'0') + month}-${(day>9?'':'0') + day}`;
+    return `${date.getFullYear()}-${(month > 9 ? '' : '0') + month}-${(day > 9 ? '' : '0') + day}`;
 }
 
 export function utcTimeStampToLocalTime(timestamp) {
-    let date = new Date(timestamp * 1000);
-    let hours = date.getHours();
-    let mins = date.getMinutes();
-    return `${(hours>9?'':'0') + hours}:${(mins>9?'':'0') + mins}`;
+    const date = new Date(timestamp * 1000);
+    const hours = date.getHours();
+    const mins = date.getMinutes();
+    return `${(hours > 9 ? '' : '0') + hours}:${(mins > 9 ? '' : '0') + mins}`;
 }
 
 export function formatDateTime(date) {
-    let month = date.getMonth() + 1;
-    let day = date.getDate();
-    let hours = date.getHours();
-    let mins = date.getMinutes();
+    const month = date.getMonth() + 1;
+    const day = date.getDate();
+    const hours = date.getHours();
+    const mins = date.getMinutes();
 
-    return `${date.getFullYear()}-${(month>9?'':'0') + month}-${(day>9?'':'0') + day} ${(hours>9?'':'0') + hours}:${(mins>9?'':'0') + mins}`;
-}
\ No newline at end of file
+    return `${date.getFullYear()}-${(month > 9 ? '' : '0') + month}-${(day > 9 ? '' : '0') + day} ${(hours > 9 ? '' : '0') + hours}:${(mins > 9 ? '' : '0') + mins}`;
+}
index 882d5228d7593abf80e4cb98c2e7c5133611d47b..17f5a803aad7a03e80f8b2a45936eba1205a1aaa 100644 (file)
@@ -5,7 +5,7 @@
  */
 export function forEach(selector, callback) {
     const elements = document.querySelectorAll(selector);
-    for (let element of elements) {
+    for (const element of elements) {
         callback(element);
     }
 }
@@ -17,7 +17,7 @@ export function forEach(selector, callback) {
  * @param {Function<Event>} callback
  */
 export function onEvents(listenerElement, events, callback) {
-    for (let eventName of events) {
+    for (const eventName of events) {
         listenerElement.addEventListener(eventName, callback);
     }
 }
@@ -35,7 +35,7 @@ export function onSelect(elements, callback) {
 
     for (const listenerElement of elements) {
         listenerElement.addEventListener('click', callback);
-        listenerElement.addEventListener('keydown', (event) => {
+        listenerElement.addEventListener('keydown', event => {
             if (event.key === 'Enter' || event.key === ' ') {
                 event.preventDefault();
                 callback(event);
@@ -58,7 +58,7 @@ export function onEnterPress(elements, callback) {
         if (event.key === 'Enter') {
             callback(event);
         }
-    }
+    };
 
     elements.forEach(e => e.addEventListener('keypress', listener));
 }
@@ -73,7 +73,7 @@ export function onEnterPress(elements, callback) {
  * @param {Function} callback
  */
 export function onChildEvent(listenerElement, childSelector, eventName, callback) {
-    listenerElement.addEventListener(eventName, function(event) {
+    listenerElement.addEventListener(eventName, event => {
         const matchingChild = event.target.closest(childSelector);
         if (matchingChild) {
             callback.call(matchingChild, event, matchingChild);
@@ -91,7 +91,7 @@ export function onChildEvent(listenerElement, childSelector, eventName, callback
 export function findText(selector, text) {
     const elements = document.querySelectorAll(selector);
     text = text.toLowerCase();
-    for (let element of elements) {
+    for (const element of elements) {
         if (element.textContent.toLowerCase().includes(text)) {
             return element;
         }
@@ -105,7 +105,7 @@ export function findText(selector, text) {
  * @param {Element} element
  */
 export function showLoading(element) {
-    element.innerHTML = `<div class="loading-container"><div></div><div></div><div></div></div>`;
+    element.innerHTML = '<div class="loading-container"><div></div><div></div><div></div></div>';
 }
 
 /**
@@ -130,4 +130,4 @@ export function htmlToDom(html) {
     wrap.innerHTML = html;
     window.$components.init(wrap);
     return wrap.children[0];
-}
\ No newline at end of file
+}
index 67ac4cc0ecb31fd51f52c785990149a17d6da5f4..efc071d3ec4b51a94b4060329b633d340b72ad97 100644 (file)
@@ -1,51 +1,12 @@
+// Docs: https://www.diagrams.net/doc/faq/embed-mode
+
 let iFrame = null;
 let lastApprovedOrigin;
-let onInit, onSave;
-
-/**
- * Show the draw.io editor.
- * @param {String} drawioUrl
- * @param {Function} onInitCallback - Must return a promise with the xml to load for the editor.
- * @param {Function} onSaveCallback - Is called with the drawing data on save.
- */
-function show(drawioUrl, onInitCallback, onSaveCallback) {
-    onInit = onInitCallback;
-    onSave = onSaveCallback;
-
-    iFrame = document.createElement('iframe');
-    iFrame.setAttribute('frameborder', '0');
-    window.addEventListener('message', drawReceive);
-    iFrame.setAttribute('src', drawioUrl);
-    iFrame.setAttribute('class', 'fullscreen');
-    iFrame.style.backgroundColor = '#FFFFFF';
-    document.body.appendChild(iFrame);
-    lastApprovedOrigin = (new URL(drawioUrl)).origin;
-}
-
-function close() {
-    drawEventClose();
-}
+let onInit; let
+    onSave;
 
-/**
- * Receive and handle a message event from the draw.io window.
- * @param {MessageEvent} event
- */
-function drawReceive(event) {
-    if (!event.data || event.data.length < 1) return;
-    if (event.origin !== lastApprovedOrigin) return;
-
-    const message = JSON.parse(event.data);
-    if (message.event === 'init') {
-        drawEventInit();
-    } else if (message.event === 'exit') {
-        drawEventClose();
-    } else if (message.event === 'save') {
-        drawEventSave(message);
-    } else if (message.event === 'export') {
-        drawEventExport(message);
-    } else if (message.event === 'configure') {
-        drawEventConfigure();
-    }
+function drawPostMessage(data) {
+    iFrame.contentWindow.postMessage(JSON.stringify(data), lastApprovedOrigin);
 }
 
 function drawEventExport(message) {
@@ -55,13 +16,15 @@ function drawEventExport(message) {
 }
 
 function drawEventSave(message) {
-    drawPostMessage({action: 'export', format: 'xmlpng', xml: message.xml, spin: 'Updating drawing'});
+    drawPostMessage({
+        action: 'export', format: 'xmlpng', xml: message.xml, spin: 'Updating drawing',
+    });
 }
 
 function drawEventInit() {
     if (!onInit) return;
     onInit().then(xml => {
-        drawPostMessage({action: 'load', autosave: 1, xml: xml});
+        drawPostMessage({action: 'load', autosave: 1, xml});
     });
 }
 
@@ -72,29 +35,72 @@ function drawEventConfigure() {
 }
 
 function drawEventClose() {
+    // eslint-disable-next-line no-use-before-define
     window.removeEventListener('message', drawReceive);
     if (iFrame) document.body.removeChild(iFrame);
 }
 
-function drawPostMessage(data) {
-    iFrame.contentWindow.postMessage(JSON.stringify(data), lastApprovedOrigin);
+/**
+ * Receive and handle a message event from the draw.io window.
+ * @param {MessageEvent} event
+ */
+function drawReceive(event) {
+    if (!event.data || event.data.length < 1) return;
+    if (event.origin !== lastApprovedOrigin) return;
+
+    const message = JSON.parse(event.data);
+    if (message.event === 'init') {
+        drawEventInit();
+    } else if (message.event === 'exit') {
+        drawEventClose();
+    } else if (message.event === 'save') {
+        drawEventSave(message);
+    } else if (message.event === 'export') {
+        drawEventExport(message);
+    } else if (message.event === 'configure') {
+        drawEventConfigure();
+    }
 }
 
-async function upload(imageData, pageUploadedToId) {
-    let data = {
+/**
+ * Show the draw.io editor.
+ * @param {String} drawioUrl
+ * @param {Function} onInitCallback - Must return a promise with the xml to load for the editor.
+ * @param {Function} onSaveCallback - Is called with the drawing data on save.
+ */
+export function show(drawioUrl, onInitCallback, onSaveCallback) {
+    onInit = onInitCallback;
+    onSave = onSaveCallback;
+
+    iFrame = document.createElement('iframe');
+    iFrame.setAttribute('frameborder', '0');
+    window.addEventListener('message', drawReceive);
+    iFrame.setAttribute('src', drawioUrl);
+    iFrame.setAttribute('class', 'fullscreen');
+    iFrame.style.backgroundColor = '#FFFFFF';
+    document.body.appendChild(iFrame);
+    lastApprovedOrigin = (new URL(drawioUrl)).origin;
+}
+
+export async function upload(imageData, pageUploadedToId) {
+    const data = {
         image: imageData,
         uploaded_to: pageUploadedToId,
     };
-    const resp = await window.$http.post(window.baseUrl(`/images/drawio`), data);
+    const resp = await window.$http.post(window.baseUrl('/images/drawio'), data);
     return resp.data;
 }
 
+export function close() {
+    drawEventClose();
+}
+
 /**
  * Load an existing image, by fetching it as Base64 from the system.
  * @param drawingId
  * @returns {Promise<string>}
  */
-async function load(drawingId) {
+export async function load(drawingId) {
     try {
         const resp = await window.$http.get(window.baseUrl(`/images/drawio/base64/${drawingId}`));
         return `data:image/png;base64,${resp.data.content}`;
@@ -106,5 +112,3 @@ async function load(drawingId) {
         throw error;
     }
 }
-
-export default {show, close, upload, load};
\ No newline at end of file
index d2dacfa7b7c3f9f50ac21a8ae6cd1e4651472f17..761305793a1cc4a91385c516edf0fcfa6acdcaf1 100644 (file)
@@ -6,13 +6,12 @@ const stack = [];
  * @param {String} eventName
  * @param {*} eventData
  */
-function emit(eventName, eventData) {
+export function emit(eventName, eventData) {
     stack.push({name: eventName, data: eventData});
-    if (typeof listeners[eventName] === 'undefined') return this;
-    let eventsToStart = listeners[eventName];
-    for (let i = 0; i < eventsToStart.length; i++) {
-        let event = eventsToStart[i];
-        event(eventData);
+
+    const listenersToRun = listeners[eventName] || [];
+    for (const listener of listenersToRun) {
+        listener(eventData);
     }
 }
 
@@ -22,7 +21,7 @@ function emit(eventName, eventData) {
  * @param {Function} callback
  * @returns {Events}
  */
-function listen(eventName, callback) {
+export function listen(eventName, callback) {
     if (typeof listeners[eventName] === 'undefined') listeners[eventName] = [];
     listeners[eventName].push(callback);
 }
@@ -34,43 +33,49 @@ function listen(eventName, callback) {
  * @param {String} eventName
  * @param {Object} eventData
  */
-function emitPublic(targetElement, eventName, eventData) {
+export function emitPublic(targetElement, eventName, eventData) {
     const event = new CustomEvent(eventName, {
         detail: eventData,
-        bubbles: true
+        bubbles: true,
     });
     targetElement.dispatchEvent(event);
 }
 
+/**
+ * Emit a success event with the provided message.
+ * @param {String} message
+ */
+export function success(message) {
+    emit('success', message);
+}
+
+/**
+ * Emit an error event with the provided message.
+ * @param {String} message
+ */
+export function error(message) {
+    emit('error', message);
+}
+
 /**
  * Notify of standard server-provided validation errors.
- * @param {Object} error
+ * @param {Object} responseErr
  */
-function showValidationErrors(error) {
-    if (!error.status) return;
-    if (error.status === 422 && error.data) {
-        const message = Object.values(error.data).flat().join('\n');
-        emit('error', message);
+export function showValidationErrors(responseErr) {
+    if (!responseErr.status) return;
+    if (responseErr.status === 422 && responseErr.data) {
+        const message = Object.values(responseErr.data).flat().join('\n');
+        error(message);
     }
 }
 
 /**
  * Notify standard server-provided error messages.
- * @param {Object} error
+ * @param {Object} responseErr
  */
-function showResponseError(error) {
-    if (!error.status) return;
-    if (error.status >= 400 && error.data && error.data.message) {
-        emit('error', error.data.message);
+export function showResponseError(responseErr) {
+    if (!responseErr.status) return;
+    if (responseErr.status >= 400 && responseErr.data && responseErr.data.message) {
+        error(responseErr.data.message);
     }
 }
-
-export default {
-    emit,
-    emitPublic,
-    listen,
-    success: (msg) => emit('success', msg),
-    error: (msg) => emit('error', msg),
-    showValidationErrors,
-    showResponseError,
-}
\ No newline at end of file
index 211ea0c92b75ceaca9fa869527e8563758bb21b6..d0d33e317df9230c2989bbcfefcd0bb2333032cf 100644 (file)
@@ -1,90 +1,48 @@
-
 /**
- * Perform a HTTP GET request.
- * Can easily pass query parameters as the second parameter.
- * @param {String} url
- * @param {Object} params
- * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>}
+ * @typedef FormattedResponse
+ * @property {Headers} headers
+ * @property {Response} original
+ * @property {Object|String} data
+ * @property {Boolean} redirected
+ * @property {Number} status
+ * @property {string} statusText
+ * @property {string} url
  */
-async function get(url, params = {}) {
-    return request(url, {
-        method: 'GET',
-        params,
-    });
-}
 
 /**
- * Perform a HTTP POST request.
- * @param {String} url
- * @param {Object} data
- * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>}
+ * Get the content from a fetch response.
+ * Checks the content-type header to determine the format.
+ * @param {Response} response
+ * @returns {Promise<Object|String>}
  */
-async function post(url, data = null) {
-    return dataRequest('POST', url, data);
-}
+async function getResponseContent(response) {
+    if (response.status === 204) {
+        return null;
+    }
 
-/**
- * Perform a HTTP PUT request.
- * @param {String} url
- * @param {Object} data
- * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>}
- */
-async function put(url, data = null) {
-    return dataRequest('PUT', url, data);
-}
+    const responseContentType = response.headers.get('Content-Type') || '';
+    const subType = responseContentType.split(';')[0].split('/').pop();
 
-/**
- * Perform a HTTP PATCH request.
- * @param {String} url
- * @param {Object} data
- * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>}
- */
-async function patch(url, data = null) {
-    return dataRequest('PATCH', url, data);
-}
+    if (subType === 'javascript' || subType === 'json') {
+        return response.json();
+    }
 
-/**
- * Perform a HTTP DELETE request.
- * @param {String} url
- * @param {Object} data
- * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>}
- */
-async function performDelete(url, data = null) {
-    return dataRequest('DELETE', url, data);
+    return response.text();
 }
 
-/**
- * Perform a HTTP request to the back-end that includes data in the body.
- * Parses the body to JSON if an object, setting the correct headers.
- * @param {String} method
- * @param {String} url
- * @param {Object} data
- * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>}
- */
-async function dataRequest(method, url, data = null) {
-    const options = {
-        method: method,
-        body: data,
-    };
+export class HttpError extends Error {
 
-    // Send data as JSON if a plain object
-    if (typeof data === 'object' && !(data instanceof FormData)) {
-        options.headers = {
-            'Content-Type': 'application/json',
-            'X-Requested-With': 'XMLHttpRequest',
-        };
-        options.body = JSON.stringify(data);
-    }
-
-    // Ensure FormData instances are sent over POST
-    // Since Laravel does not read multipart/form-data from other types
-    // of request. Hence the addition of the magic _method value.
-    if (data instanceof FormData && method !== 'post') {
-        data.append('_method', method);
-        options.method = 'post';
+    constructor(response, content) {
+        super(response.statusText);
+        this.data = content;
+        this.headers = response.headers;
+        this.redirected = response.redirected;
+        this.status = response.status;
+        this.statusText = response.statusText;
+        this.url = response.url;
+        this.original = response;
     }
 
-    return request(url, options)
 }
 
 /**
@@ -92,34 +50,35 @@ async function dataRequest(method, url, data = null) {
  * to communicate with the back-end. Parses & formats the response.
  * @param {String} url
  * @param {Object} options
- * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>}
+ * @returns {Promise<FormattedResponse>}
  */
 async function request(url, options = {}) {
-    if (!url.startsWith('http')) {
-        url = window.baseUrl(url);
+    let requestUrl = url;
+
+    if (!requestUrl.startsWith('http')) {
+        requestUrl = window.baseUrl(requestUrl);
     }
 
     if (options.params) {
-        const urlObj = new URL(url);
-        for (let paramName of Object.keys(options.params)) {
+        const urlObj = new URL(requestUrl);
+        for (const paramName of Object.keys(options.params)) {
             const value = options.params[paramName];
             if (typeof value !== 'undefined' && value !== null) {
                 urlObj.searchParams.set(paramName, value);
             }
         }
-        url = urlObj.toString();
+        requestUrl = urlObj.toString();
     }
 
     const csrfToken = document.querySelector('meta[name=token]').getAttribute('content');
-    options = Object.assign({}, options, {
-        'credentials': 'same-origin',
-    });
-    options.headers = Object.assign({}, options.headers || {}, {
-        'baseURL': window.baseUrl(''),
+    const requestOptions = {...options, credentials: 'same-origin'};
+    requestOptions.headers = {
+        ...requestOptions.headers || {},
+        baseURL: window.baseUrl(''),
         'X-CSRF-TOKEN': csrfToken,
-    });
+    };
 
-    const response = await fetch(url, options);
+    const response = await fetch(requestUrl, requestOptions);
     const content = await getResponseContent(response);
     const returnData = {
         data: content,
@@ -139,44 +98,91 @@ async function request(url, options = {}) {
 }
 
 /**
- * Get the content from a fetch response.
- * Checks the content-type header to determine the format.
- * @param {Response} response
- * @returns {Promise<Object|String>}
+ * Perform a HTTP request to the back-end that includes data in the body.
+ * Parses the body to JSON if an object, setting the correct headers.
+ * @param {String} method
+ * @param {String} url
+ * @param {Object} data
+ * @returns {Promise<FormattedResponse>}
  */
-async function getResponseContent(response) {
-    if (response.status === 204) {
-        return null;
-    }
+async function dataRequest(method, url, data = null) {
+    const options = {
+        method,
+        body: data,
+    };
 
-    const responseContentType = response.headers.get('Content-Type') || '';
-    const subType = responseContentType.split(';')[0].split('/').pop();
+    // Send data as JSON if a plain object
+    if (typeof data === 'object' && !(data instanceof FormData)) {
+        options.headers = {
+            'Content-Type': 'application/json',
+            'X-Requested-With': 'XMLHttpRequest',
+        };
+        options.body = JSON.stringify(data);
+    }
 
-    if (subType === 'javascript' || subType === 'json') {
-        return await response.json();
+    // Ensure FormData instances are sent over POST
+    // Since Laravel does not read multipart/form-data from other types
+    // of request. Hence the addition of the magic _method value.
+    if (data instanceof FormData && method !== 'post') {
+        data.append('_method', method);
+        options.method = 'post';
     }
 
-    return await response.text();
+    return request(url, options);
 }
 
-class HttpError extends Error {
-    constructor(response, content) {
-        super(response.statusText);
-        this.data = content;
-        this.headers = response.headers;
-        this.redirected = response.redirected;
-        this.status = response.status;
-        this.statusText = response.statusText;
-        this.url = response.url;
-        this.original = response;
-    }
+/**
+ * Perform a HTTP GET request.
+ * Can easily pass query parameters as the second parameter.
+ * @param {String} url
+ * @param {Object} params
+ * @returns {Promise<FormattedResponse>}
+ */
+export async function get(url, params = {}) {
+    return request(url, {
+        method: 'GET',
+        params,
+    });
+}
+
+/**
+ * Perform a HTTP POST request.
+ * @param {String} url
+ * @param {Object} data
+ * @returns {Promise<FormattedResponse>}
+ */
+export async function post(url, data = null) {
+    return dataRequest('POST', url, data);
+}
+
+/**
+ * Perform a HTTP PUT request.
+ * @param {String} url
+ * @param {Object} data
+ * @returns {Promise<FormattedResponse>}
+ */
+export async function put(url, data = null) {
+    return dataRequest('PUT', url, data);
+}
+
+/**
+ * Perform a HTTP PATCH request.
+ * @param {String} url
+ * @param {Object} data
+ * @returns {Promise<FormattedResponse>}
+ */
+export async function patch(url, data = null) {
+    return dataRequest('PATCH', url, data);
+}
+
+/**
+ * Perform a HTTP DELETE request.
+ * @param {String} url
+ * @param {Object} data
+ * @returns {Promise<FormattedResponse>}
+ */
+async function performDelete(url, data = null) {
+    return dataRequest('DELETE', url, data);
 }
 
-export default {
-    get: get,
-    post: post,
-    put: put,
-    patch: patch,
-    delete: performDelete,
-    HttpError: HttpError,
-};
\ No newline at end of file
+export {performDelete as delete};
index 0f866ceaaacd368ad317eb7e14a7b572ce96f93e..34111bb2d37886e3d98a7e150fd7ada800ba186b 100644 (file)
@@ -57,7 +57,6 @@ export class KeyboardNavigationHandler {
      * @param {KeyboardEvent} event
      */
     #keydownHandler(event) {
-
         // Ignore certain key events in inputs to allow text editing.
         if (event.target.matches('input') && (event.key === 'ArrowRight' || event.key === 'ArrowLeft')) {
             return;
@@ -72,7 +71,7 @@ export class KeyboardNavigationHandler {
         } else if (event.key === 'Escape') {
             if (this.onEscape) {
                 this.onEscape(event);
-            } else if  (document.activeElement) {
+            } else if (document.activeElement) {
                 document.activeElement.blur();
             }
         } else if (event.key === 'Enter' && this.onEnter) {
@@ -88,8 +87,9 @@ export class KeyboardNavigationHandler {
         const focusable = [];
         const selector = '[tabindex]:not([tabindex="-1"]),[href],button:not([tabindex="-1"],[disabled]),input:not([type=hidden])';
         for (const container of this.containers) {
-            focusable.push(...container.querySelectorAll(selector))
+            focusable.push(...container.querySelectorAll(selector));
         }
         return focusable;
     }
-}
\ No newline at end of file
+
+}
index ea82f993e8f82dcee5673f13bb183f9bc74e3d51..d5e6fa7987438e1ab771ec63dba124d463440540 100644 (file)
@@ -4,7 +4,7 @@
  * @returns {string}
  */
 export function kebabToCamel(kebab) {
-    const ucFirst = (word) => word.slice(0,1).toUpperCase() + word.slice(1);
+    const ucFirst = word => word.slice(0, 1).toUpperCase() + word.slice(1);
     const words = kebab.split('-');
     return words[0] + words.slice(1).map(ucFirst).join('');
 }
@@ -15,5 +15,5 @@ export function kebabToCamel(kebab) {
  * @returns {String}
  */
 export function camelToKebab(camelStr) {
-    return camelStr.replace(/[A-Z]/g, (str, offset) =>  (offset > 0 ? '-' : '') + str.toLowerCase());
-}
\ No newline at end of file
+    return camelStr.replace(/[A-Z]/g, (str, offset) => (offset > 0 ? '-' : '') + str.toLowerCase());
+}
index 62bb51f56aacb5f0216e0e4621ffdfcae0d34481..e562a9152e91924e4707f63ce43e4460648b4a7a 100644 (file)
@@ -5,11 +5,7 @@
  */
 class Translator {
 
-    /**
-     * Create an instance, Passing in the required translations
-     * @param translations
-     */
-    constructor(translations) {
+    constructor() {
         this.store = new Map();
         this.parseTranslations();
     }
@@ -19,7 +15,7 @@ class Translator {
      */
     parseTranslations() {
         const translationMetaTags = document.querySelectorAll('meta[name="translation"]');
-        for (let tag of translationMetaTags) {
+        for (const tag of translationMetaTags) {
             const key = tag.getAttribute('key');
             const value = tag.getAttribute('value');
             this.store.set(key, value);
@@ -27,7 +23,7 @@ class Translator {
     }
 
     /**
-     * Get a translation, Same format as laravel's 'trans' helper
+     * Get a translation, Same format as Laravel's 'trans' helper
      * @param key
      * @param replacements
      * @returns {*}
@@ -38,8 +34,8 @@ class Translator {
     }
 
     /**
-     * Get pluralised text, Dependant on the given count.
-     * Same format at laravel's 'trans_choice' helper.
+     * Get pluralised text, Dependent on the given count.
+     * Same format at Laravel's 'trans_choice' helper.
      * @param key
      * @param count
      * @param replacements
@@ -52,7 +48,7 @@ class Translator {
 
     /**
      * Parse the given translation and find the correct plural option
-     * to use. Similar format at laravel's 'trans_choice' helper.
+     * to use. Similar format at Laravel's 'trans_choice' helper.
      * @param {String} translation
      * @param {Number} count
      * @param {Object} replacements
@@ -64,7 +60,7 @@ class Translator {
         const rangeRegex = /^\[([0-9]+),([0-9*]+)]/;
         let result = null;
 
-        for (let t of splitText) {
+        for (const t of splitText) {
             // Parse exact matches
             const exactMatches = t.match(exactCountRegex);
             if (exactMatches !== null && Number(exactMatches[1]) === count) {
@@ -117,14 +113,17 @@ class Translator {
      */
     performReplacements(string, replacements) {
         if (!replacements) return string;
-        const replaceMatches = string.match(/:([\S]+)/g);
+        const replaceMatches = string.match(/:(\S+)/g);
         if (replaceMatches === null) return string;
+        let updatedString = string;
+
         replaceMatches.forEach(match => {
             const key = match.substring(1);
             if (typeof replacements[key] === 'undefined') return;
-            string = string.replace(match, replacements[key]);
+            updatedString = updatedString.replace(match, replacements[key]);
         });
-        return string;
+
+        return updatedString;
     }
 
 }
index 238f8b1d88c7b4d1f2be0d684a9f816be9461949..dd97d81aaf47113b301660794eda9b881d54e1e8 100644 (file)
@@ -1,5 +1,3 @@
-
-
 /**
  * Returns a function, that, as long as it continues to be invoked, will not
  * be triggered. The function will be called after it stops being called for
@@ -13,9 +11,9 @@
  */
 export function debounce(func, wait, immediate) {
     let timeout;
-    return function() {
-        const context = this, args = arguments;
-        const later = function() {
+    return function debouncedWrapper(...args) {
+        const context = this;
+        const later = function debouncedTimeout() {
             timeout = null;
             if (!immediate) func.apply(context, args);
         };
@@ -24,7 +22,7 @@ export function debounce(func, wait, immediate) {
         timeout = setTimeout(later, wait);
         if (callNow) func.apply(context, args);
     };
-};
+}
 
 /**
  * Scroll and highlight an element.
@@ -55,11 +53,11 @@ export function scrollAndHighlightElement(element) {
  */
 export function escapeHtml(unsafe) {
     return unsafe
-        .replace(/&/g, "&amp;")
-        .replace(/</g, "&lt;")
-        .replace(/>/g, "&gt;")
-        .replace(/"/g, "&quot;")
-        .replace(/'/g, "&#039;");
+        .replace(/&/g, '&amp;')
+        .replace(/</g, '&lt;')
+        .replace(/>/g, '&gt;')
+        .replace(/"/g, '&quot;')
+        .replace(/'/g, '&#039;');
 }
 
 /**
@@ -68,6 +66,7 @@ export function escapeHtml(unsafe) {
  * @returns {string}
  */
 export function uniqueId() {
-    const S4 = () => (((1+Math.random())*0x10000)|0).toString(16).substring(1);
-    return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
-}
\ No newline at end of file
+    // eslint-disable-next-line no-bitwise
+    const S4 = () => (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
+    return (`${S4() + S4()}-${S4()}-${S4()}-${S4()}-${S4()}${S4()}${S4()}`);
+}
index 89a809084caaace4971ce98afbc56fd45407231d..2095cd2ca4f3ff73cff6c3fe40808fc53f1b96b7 100644 (file)
@@ -1,8 +1,8 @@
 import {
     init,
     attributesModule,
-    toVNode
-} from "snabbdom";
+    toVNode,
+} from 'snabbdom';
 
 let patcher;
 
@@ -12,7 +12,6 @@ let patcher;
 function getPatcher() {
     if (patcher) return patcher;
 
-
     patcher = init([
         attributesModule,
     ]);
@@ -28,4 +27,4 @@ export function patchDomFromHtmlString(domTarget, html) {
     const contentDom = document.createElement('div');
     contentDom.innerHTML = html;
     getPatcher()(toVNode(domTarget), toVNode(contentDom));
-}
\ No newline at end of file
+}
index a25debac123d75c515814400f75ee44144600f40..d0a5acdc24d8ffd67204e54b7f11b0f299e81243 100644 (file)
@@ -2,7 +2,6 @@
  * @param {Editor} editor
  */
 export function listen(editor) {
-
     // Replace editor content
     window.$events.listen('editor::replace', ({html}) => {
         editor.setContent(html);
@@ -31,4 +30,4 @@ export function listen(editor) {
             editor.focus();
         }
     });
-}
\ No newline at end of file
+}
index 85c1919d494e8bcd776dcbe9fd93ebebf5b61184..a0e7156ee6866bdbecdbe9dd43f057aa26b228ff 100644 (file)
@@ -1,32 +1,35 @@
-import {register as registerShortcuts} from "./shortcuts";
-import {listen as listenForCommonEvents} from "./common-events";
-import {scrollToQueryString} from "./scrolling";
-import {listenForDragAndPaste} from "./drop-paste-handling";
-import {getPrimaryToolbar, registerAdditionalToolbars} from "./toolbars";
-import {registerCustomIcons} from "./icons";
+import {register as registerShortcuts} from './shortcuts';
+import {listen as listenForCommonEvents} from './common-events';
+import {scrollToQueryString} from './scrolling';
+import {listenForDragAndPaste} from './drop-paste-handling';
+import {getPrimaryToolbar, registerAdditionalToolbars} from './toolbars';
+import {registerCustomIcons} from './icons';
 
-import {getPlugin as getCodeeditorPlugin} from "./plugin-codeeditor";
-import {getPlugin as getDrawioPlugin} from "./plugin-drawio";
-import {getPlugin as getCustomhrPlugin} from "./plugins-customhr";
-import {getPlugin as getImagemanagerPlugin} from "./plugins-imagemanager";
-import {getPlugin as getAboutPlugin} from "./plugins-about";
-import {getPlugin as getDetailsPlugin} from "./plugins-details";
-import {getPlugin as getTasklistPlugin} from "./plugins-tasklist";
+import {getPlugin as getCodeeditorPlugin} from './plugin-codeeditor';
+import {getPlugin as getDrawioPlugin} from './plugin-drawio';
+import {getPlugin as getCustomhrPlugin} from './plugins-customhr';
+import {getPlugin as getImagemanagerPlugin} from './plugins-imagemanager';
+import {getPlugin as getAboutPlugin} from './plugins-about';
+import {getPlugin as getDetailsPlugin} from './plugins-details';
+import {getPlugin as getTasklistPlugin} from './plugins-tasklist';
 
-const style_formats = [
-    {title: "Large Header", format: "h2", preview: 'color: blue;'},
-    {title: "Medium Header", format: "h3"},
-    {title: "Small Header", format: "h4"},
-    {title: "Tiny Header", format: "h5"},
-    {title: "Paragraph", format: "p", exact: true, classes: ''},
-    {title: "Blockquote", format: "blockquote"},
+const styleFormats = [
+    {title: 'Large Header', format: 'h2', preview: 'color: blue;'},
+    {title: 'Medium Header', format: 'h3'},
+    {title: 'Small Header', format: 'h4'},
+    {title: 'Tiny Header', format: 'h5'},
     {
-        title: "Callouts", items: [
-            {title: "Information", format: 'calloutinfo'},
-            {title: "Success", format: 'calloutsuccess'},
-            {title: "Warning", format: 'calloutwarning'},
-            {title: "Danger", format: 'calloutdanger'}
-        ]
+        title: 'Paragraph', format: 'p', exact: true, classes: '',
+    },
+    {title: 'Blockquote', format: 'blockquote'},
+    {
+        title: 'Callouts',
+        items: [
+            {title: 'Information', format: 'calloutinfo'},
+            {title: 'Success', format: 'calloutsuccess'},
+            {title: 'Warning', format: 'calloutwarning'},
+            {title: 'Danger', format: 'calloutdanger'},
+        ],
     },
 ];
 
@@ -37,10 +40,10 @@ const formats = {
     calloutsuccess: {block: 'p', exact: true, attributes: {class: 'callout success'}},
     calloutinfo: {block: 'p', exact: true, attributes: {class: 'callout info'}},
     calloutwarning: {block: 'p', exact: true, attributes: {class: 'callout warning'}},
-    calloutdanger: {block: 'p', exact: true, attributes: {class: 'callout danger'}}
+    calloutdanger: {block: 'p', exact: true, attributes: {class: 'callout danger'}},
 };
 
-const color_map = [
+const colorMap = [
     '#BFEDD2', '',
     '#FBEEB8', '',
     '#F8CAC6', '',
@@ -66,14 +69,13 @@ const color_map = [
     '#34495E', '',
 
     '#000000', '',
-    '#ffffff', ''
+    '#ffffff', '',
 ];
 
-function file_picker_callback(callback, value, meta) {
-
+function filePickerCallback(callback, value, meta) {
     // field_name, url, type, win
     if (meta.filetype === 'file') {
-        /** @type {EntitySelectorPopup} **/
+        /** @type {EntitySelectorPopup} * */
         const selector = window.$components.first('entity-selector-popup');
         selector.show(entity => {
             callback(entity.link, {
@@ -85,13 +87,12 @@ function file_picker_callback(callback, value, meta) {
 
     if (meta.filetype === 'image') {
         // Show image manager
-        /** @type {ImageManager} **/
+        /** @type {ImageManager} * */
         const imageManager = window.$components.first('image-manager');
-        imageManager.show(function (image) {
+        imageManager.show(image => {
             callback(image.url, {alt: image.name});
         }, 'gallery');
     }
-
 }
 
 /**
@@ -100,30 +101,30 @@ function file_picker_callback(callback, value, meta) {
  */
 function gatherPlugins(options) {
     const plugins = [
-        "image",
-        "table",
-        "link",
-        "autolink",
-        "fullscreen",
-        "code",
-        "customhr",
-        "autosave",
-        "lists",
-        "codeeditor",
-        "media",
-        "imagemanager",
-        "about",
-        "details",
-        "tasklist",
+        'image',
+        'table',
+        'link',
+        'autolink',
+        'fullscreen',
+        'code',
+        'customhr',
+        'autosave',
+        'lists',
+        'codeeditor',
+        'media',
+        'imagemanager',
+        'about',
+        'details',
+        'tasklist',
         options.textDirection === 'rtl' ? 'directionality' : '',
     ];
 
-    window.tinymce.PluginManager.add('codeeditor', getCodeeditorPlugin(options));
-    window.tinymce.PluginManager.add('customhr', getCustomhrPlugin(options));
-    window.tinymce.PluginManager.add('imagemanager', getImagemanagerPlugin(options));
-    window.tinymce.PluginManager.add('about', getAboutPlugin(options));
-    window.tinymce.PluginManager.add('details', getDetailsPlugin(options));
-    window.tinymce.PluginManager.add('tasklist', getTasklistPlugin(options));
+    window.tinymce.PluginManager.add('codeeditor', getCodeeditorPlugin());
+    window.tinymce.PluginManager.add('customhr', getCustomhrPlugin());
+    window.tinymce.PluginManager.add('imagemanager', getImagemanagerPlugin());
+    window.tinymce.PluginManager.add('about', getAboutPlugin());
+    window.tinymce.PluginManager.add('details', getDetailsPlugin());
+    window.tinymce.PluginManager.add('tasklist', getTasklistPlugin());
 
     if (options.drawioUrl) {
         window.tinymce.PluginManager.add('drawio', getDrawioPlugin(options));
@@ -137,11 +138,11 @@ function gatherPlugins(options) {
  * Fetch custom HTML head content from the parent page head into the editor.
  */
 function fetchCustomHeadContent() {
-    const headContentLines = document.head.innerHTML.split("\n");
+    const headContentLines = document.head.innerHTML.split('\n');
     const startLineIndex = headContentLines.findIndex(line => line.trim() === '<!-- Start: custom user content -->');
     const endLineIndex = headContentLines.findIndex(line => line.trim() === '<!-- End: custom user content -->');
     if (startLineIndex === -1 || endLineIndex === -1) {
-        return ''
+        return '';
     }
     return headContentLines.slice(startLineIndex + 1, endLineIndex).join('\n');
 }
@@ -152,10 +153,10 @@ function fetchCustomHeadContent() {
  * @param {Editor} editor
  */
 function setupBrFilter(editor) {
-    editor.serializer.addNodeFilter('br', function(nodes) {
+    editor.serializer.addNodeFilter('br', nodes => {
         for (const node of nodes) {
             if (node.parent && node.parent.name === 'code') {
-                const newline = tinymce.html.Node.create('#text');
+                const newline = window.tinymce.html.Node.create('#text');
                 newline.value = '\n';
                 node.replace(newline);
             }
@@ -168,7 +169,14 @@ function setupBrFilter(editor) {
  * @return {function(Editor)}
  */
 function getSetupCallback(options) {
-    return function(editor) {
+    return function setupCallback(editor) {
+        function editorChange() {
+            if (options.darkMode) {
+                editor.contentDocument.documentElement.classList.add('dark-mode');
+            }
+            window.$events.emit('editor-html-change', '');
+        }
+
         editor.on('ExecCommand change input NodeChange ObjectResized', editorChange);
         listenForCommonEvents(editor);
         listenForDragAndPaste(editor, options);
@@ -184,13 +192,6 @@ function getSetupCallback(options) {
             setupBrFilter(editor);
         });
 
-        function editorChange() {
-            if (options.darkMode) {
-                editor.contentDocument.documentElement.classList.add('dark-mode');
-            }
-            window.$events.emit('editor-html-change', '');
-        }
-
         // Custom handler hook
         window.$events.emitPublic(options.containerElement, 'editor-tinymce::setup', {editor});
 
@@ -200,9 +201,9 @@ function getSetupCallback(options) {
             icon: 'sourcecode',
             onAction() {
                 editor.execCommand('mceToggleFormat', false, 'code');
-            }
-        })
-    }
+            },
+        });
+    };
 }
 
 /**
@@ -229,7 +230,6 @@ body {
  * @return {Object}
  */
 export function build(options) {
-
     // Set language
     window.tinymce.addI18n(options.language, options.translationMap);
 
@@ -241,7 +241,7 @@ export function build(options) {
         width: '100%',
         height: '100%',
         selector: '#html-editor',
-        cache_suffix: '?version=' + version,
+        cache_suffix: `?version=${version}`,
         content_css: [
             window.baseUrl('/dist/styles.css'),
         ],
@@ -263,18 +263,18 @@ export function build(options) {
         automatic_uploads: false,
         custom_elements: 'doc-root,code-block',
         valid_children: [
-            "-div[p|h1|h2|h3|h4|h5|h6|blockquote|code-block]",
-            "+div[pre|img]",
-            "-doc-root[doc-root|#text]",
-            "-li[details]",
-            "+code-block[pre]",
-            "+doc-root[p|h1|h2|h3|h4|h5|h6|blockquote|code-block|div]"
+            '-div[p|h1|h2|h3|h4|h5|h6|blockquote|code-block]',
+            '+div[pre|img]',
+            '-doc-root[doc-root|#text]',
+            '-li[details]',
+            '+code-block[pre]',
+            '+doc-root[p|h1|h2|h3|h4|h5|h6|blockquote|code-block|div]',
         ].join(','),
         plugins: gatherPlugins(options),
         contextmenu: false,
         toolbar: getPrimaryToolbar(options),
         content_style: getContentStyle(options),
-        style_formats,
+        style_formats: styleFormats,
         style_formats_merge: false,
         media_alt_source: false,
         media_poster: false,
@@ -282,10 +282,10 @@ export function build(options) {
         table_style_by_css: true,
         table_use_colgroups: true,
         file_picker_types: 'file image',
-        color_map,
-        file_picker_callback,
+        color_map: colorMap,
+        file_picker_callback: filePickerCallback,
         paste_preprocess(plugin, args) {
-            const content = args.content;
+            const {content} = args;
             if (content.indexOf('<img src="file://') !== -1) {
                 args.content = '';
             }
@@ -296,7 +296,7 @@ export function build(options) {
         },
         setup(editor) {
             registerCustomIcons(editor);
-            registerAdditionalToolbars(editor, options);
+            registerAdditionalToolbars(editor);
             getSetupCallback(options)(editor);
         },
     };
@@ -312,4 +312,4 @@ export function build(options) {
  * @property {int} pageId
  * @property {Object} translations
  * @property {Object} translationMap
- */
\ No newline at end of file
+ */
index 866d25a24e828a5fbcbf99ab1bb51a950563bc69..33078cd1d5687b74fb877ef8dd2719dd4e0f59ff 100644 (file)
@@ -1,4 +1,4 @@
-import Clipboard from "../services/clipboard";
+import {Clipboard} from '../services/clipboard';
 
 let wrap;
 let draggedContentEditable;
@@ -7,6 +7,25 @@ function hasTextContent(node) {
     return node && !!(node.textContent || node.innerText);
 }
 
+/**
+ * Upload an image file to the server
+ * @param {File} file
+ * @param {int} pageId
+ */
+async function uploadImageFile(file, pageId) {
+    if (file === null || file.type.indexOf('image') !== 0) {
+        throw new Error('Not an image file');
+    }
+
+    const remoteFilename = file.name || `image-${Date.now()}.png`;
+    const formData = new FormData();
+    formData.append('file', file, remoteFilename);
+    formData.append('uploaded_to', pageId);
+
+    const resp = await window.$http.post(window.baseUrl('/images/gallery'), formData);
+    return resp.data;
+}
+
 /**
  * Handle pasting images from clipboard.
  * @param {Editor} editor
@@ -23,8 +42,7 @@ function paste(editor, options, event) {
 
     const images = clipboard.getImages();
     for (const imageFile of images) {
-
-        const id = "image-" + Math.random().toString(16).slice(2);
+        const id = `image-${Math.random().toString(16).slice(2)}`;
         const loadingImage = window.baseUrl('/loading.gif');
         event.preventDefault();
 
@@ -44,37 +62,17 @@ function paste(editor, options, event) {
             }).catch(err => {
                 editor.dom.remove(id);
                 window.$events.emit('error', options.translations.imageUploadErrorText);
-                console.log(err);
+                console.error(err);
             });
         }, 10);
     }
 }
 
-/**
- * Upload an image file to the server
- * @param {File} file
- * @param {int} pageId
- */
-async function uploadImageFile(file, pageId) {
-    if (file === null || file.type.indexOf('image') !== 0) {
-        throw new Error(`Not an image file`);
-    }
-
-    const remoteFilename = file.name || `image-${Date.now()}.png`;
-    const formData = new FormData();
-    formData.append('file', file, remoteFilename);
-    formData.append('uploaded_to', pageId);
-
-    const resp = await window.$http.post(window.baseUrl('/images/gallery'), formData);
-    return resp.data;
-}
-
 /**
  * @param {Editor} editor
- * @param {WysiwygConfigOptions} options
  */
-function dragStart(editor, options) {
-    let node = editor.selection.getNode();
+function dragStart(editor) {
+    const node = editor.selection.getNode();
 
     if (node.nodeName === 'IMG') {
         wrap = editor.dom.getParent(node, '.mceTemp');
@@ -96,8 +94,12 @@ function dragStart(editor, options) {
  * @param {DragEvent} event
  */
 function drop(editor, options, event) {
-    let dom = editor.dom,
-        rng = tinymce.dom.RangeUtils.getCaretRangeFromPoint(event.clientX, event.clientY, editor.getDoc());
+    const {dom} = editor;
+    const rng = window.tinymce.dom.RangeUtils.getCaretRangeFromPoint(
+        event.clientX,
+        event.clientY,
+        editor.getDoc(),
+    );
 
     // Template insertion
     const templateId = event.dataTransfer && event.dataTransfer.getData('bookstack/template');
@@ -105,7 +107,7 @@ function drop(editor, options, event) {
         event.preventDefault();
         window.$http.get(`/templates/${templateId}`).then(resp => {
             editor.selection.setRng(rng);
-            editor.undoManager.transact(function () {
+            editor.undoManager.transact(() => {
                 editor.execCommand('mceInsertContent', false, resp.data.html);
             });
         });
@@ -117,7 +119,7 @@ function drop(editor, options, event) {
     } else if (wrap) {
         event.preventDefault();
 
-        editor.undoManager.transact(function () {
+        editor.undoManager.transact(() => {
             editor.selection.setRng(rng);
             editor.selection.setNode(wrap);
             dom.remove(wrap);
@@ -127,7 +129,7 @@ function drop(editor, options, event) {
     // Handle contenteditable section drop
     if (!event.isDefaultPrevented() && draggedContentEditable) {
         event.preventDefault();
-        editor.undoManager.transact(function () {
+        editor.undoManager.transact(() => {
             const selectedNode = editor.selection.getNode();
             const range = editor.selection.getRng();
             const selectedNodeRoot = selectedNode.closest('body > *');
@@ -152,7 +154,7 @@ function drop(editor, options, event) {
  * @param {WysiwygConfigOptions} options
  */
 export function listenForDragAndPaste(editor, options) {
-    editor.on('dragstart', () => dragStart(editor, options));
-    editor.on('drop',  event => drop(editor, options, event));
+    editor.on('dragstart', () => dragStart(editor));
+    editor.on('drop', event => drop(editor, options, event));
     editor.on('paste', event => paste(editor, options, event));
-}
\ No newline at end of file
+}
index 2c2457fe15f585bf15569c6767a85bfca03145f1..3045df2278e8aae4e9c51548b364099ae5bacc9d 100644 (file)
@@ -5,17 +5,15 @@ const icons = {
     'table-insert-column-before': '<svg width="24" height="24"><path d="M8 19h5V5H8C6.764 5 6.766 3 8 3h11a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H8c-1.229 0-1.236-2 0-2zm7-6v6h4v-6zm0-8v6h4V5ZM3.924 11h2V9c0-1.333 2-1.333 2 0v2h2c1.335 0 1.335 2 0 2h-2v2c0 1.333-2 1.333-2 0v-2h-1.9c-1.572 0-1.113-2-.1-2z"/></svg>',
     'table-insert-row-above': '<svg width="24" height="24"><path d="M5 8v5h14V8c0-1.235 2-1.234 2 0v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8C3 6.77 5 6.764 5 8zm6 7H5v4h6zm8 0h-6v4h6zM13 3.924v2h2c1.333 0 1.333 2 0 2h-2v2c0 1.335-2 1.335-2 0v-2H9c-1.333 0-1.333-2 0-2h2v-1.9c0-1.572 2-1.113 2-.1z"/></svg>',
     'table-insert-row-after': '<svg width="24" height="24"><path d="M19 16v-5H5v5c0 1.235-2 1.234-2 0V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v11c0 1.229-2 1.236-2 0zm-6-7h6V5h-6zM5 9h6V5H5Zm6 11.076v-2H9c-1.333 0-1.333-2 0-2h2v-2c0-1.335 2-1.335 2 0v2h2c1.333 0 1.333 2 0 2h-2v1.9c0 1.572-2 1.113-2 .1z"/></svg>',
-    'table': '<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg"><path d="M19 3a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5c0-1.1.9-2 2-2ZM5 14v5h6v-5zm14 0h-6v5h6zm0-7h-6v5h6zM5 12h6V7H5Z"/></svg>',
+    table: '<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg"><path d="M19 3a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5c0-1.1.9-2 2-2ZM5 14v5h6v-5zm14 0h-6v5h6zm0-7h-6v5h6zM5 12h6V7H5Z"/></svg>',
     'table-delete-table': '<svg width="24" height="24"><path d="M5 21a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v14c0 1.1-.9 2-2 2zm0-2h14V5H5v14z"/><path d="m13.711 15.423-1.71-1.712-1.712 1.712c-1.14 1.14-2.852-.57-1.71-1.712l1.71-1.71-1.71-1.712c-1.143-1.142.568-2.853 1.71-1.71L12 10.288l1.711-1.71c1.141-1.142 2.852.57 1.712 1.71L13.71 12l1.626 1.626c1.345 1.345-.76 2.663-1.626 1.797z" style="fill-rule:nonzero;stroke-width:1.20992"/></svg>',
 };
 
-
 /**
  * @param {Editor} editor
  */
 export function registerCustomIcons(editor) {
-
     for (const [name, svg] of Object.entries(icons)) {
         editor.ui.registry.addIcon(name, svg);
     }
-}
\ No newline at end of file
+}
index cd0078b1d914da39ccdf3df993527f44f7ce4626..fa3804ea8368b8a8afd52631937de2023fe95606 100644 (file)
@@ -10,8 +10,8 @@ function elemIsCodeBlock(elem) {
  */
 function showPopup(editor, code, language, callback) {
     window.$components.first('code-editor').open(code, language, (newCode, newLang) => {
-        callback(newCode, newLang)
-        editor.focus()
+        callback(newCode, newLang);
+        editor.focus();
     });
 }
 
@@ -36,6 +36,12 @@ function defineCodeBlockCustomElement(editor) {
     const win = doc.defaultView;
 
     class CodeBlockElement extends win.HTMLElement {
+
+        /**
+         * @type {?SimpleEditorInterface}
+         */
+        editor = null;
+
         constructor() {
             super();
             this.attachShadow({mode: 'open'});
@@ -47,12 +53,13 @@ function defineCodeBlockCustomElement(editor) {
             cmContainer.style.pointerEvents = 'none';
             cmContainer.contentEditable = 'false';
             cmContainer.classList.add('CodeMirrorContainer');
+            cmContainer.classList.toggle('dark-mode', document.documentElement.classList.contains('dark-mode'));
 
             this.shadowRoot.append(...copiedStyles, cmContainer);
         }
 
         getLanguage() {
-            const getLanguageFromClassList = (classes) => {
+            const getLanguageFromClassList = classes => {
                 const langClasses = classes.split(' ').filter(cssClass => cssClass.startsWith('language-'));
                 return (langClasses[0] || '').replace('language-', '');
             };
@@ -63,11 +70,9 @@ function defineCodeBlockCustomElement(editor) {
         }
 
         setContent(content, language) {
-            if (this.cm) {
-                importVersioned('code').then(Code => {
-                    Code.setContent(this.cm, content);
-                    Code.setMode(this.cm, language, content);
-                });
+            if (this.editor) {
+                this.editor.setContent(content);
+                this.editor.setMode(language, content);
             }
 
             let pre = this.querySelector('pre');
@@ -98,7 +103,7 @@ function defineCodeBlockCustomElement(editor) {
 
         connectedCallback() {
             const connectedTime = Date.now();
-            if (this.cm) {
+            if (this.editor) {
                 return;
             }
 
@@ -109,15 +114,16 @@ function defineCodeBlockCustomElement(editor) {
             this.style.height = `${height}px`;
 
             const container = this.shadowRoot.querySelector('.CodeMirrorContainer');
-            const renderCodeMirror = (Code) => {
-                this.cm = Code.wysiwygView(container, content, this.getLanguage());
-                setTimeout(() => Code.updateLayout(this.cm), 10);
-                setTimeout(() => this.style.height = null, 12);
+            const renderEditor = Code => {
+                this.editor = Code.wysiwygView(container, this.shadowRoot, content, this.getLanguage());
+                setTimeout(() => {
+                    this.style.height = null;
+                }, 12);
             };
 
-            window.importVersioned('code').then((Code) => {
+            window.importVersioned('code').then(Code => {
                 const timeout = (Date.now() - connectedTime < 20) ? 20 : 0;
-                setTimeout(() => renderCodeMirror(Code), timeout);
+                setTimeout(() => renderEditor(Code), timeout);
             });
         }
 
@@ -131,26 +137,24 @@ function defineCodeBlockCustomElement(editor) {
                 }
             }
         }
+
     }
 
     win.customElements.define('code-block', CodeBlockElement);
 }
 
-
 /**
  * @param {Editor} editor
- * @param {String} url
  */
-function register(editor, url) {
-
-    editor.ui.registry.addIcon('codeblock', '<svg width="24" height="24"><path d="M4 3h16c.6 0 1 .4 1 1v16c0 .6-.4 1-1 1H4a1 1 0 0 1-1-1V4c0-.6.4-1 1-1Zm1 2v14h14V5Z"/><path d="M11.103 15.423c.277.277.277.738 0 .922a.692.692 0 0 1-1.106 0l-4.057-3.78a.738.738 0 0 1 0-1.107l4.057-3.872c.276-.277.83-.277 1.106 0a.724.724 0 0 1 0 1.014L7.6 12.012ZM12.897 8.577c-.245-.312-.2-.675.08-.955.28-.281.727-.27 1.027.033l4.057 3.78a.738.738 0 0 1 0 1.107l-4.057 3.872c-.277.277-.83.277-1.107 0a.724.724 0 0 1 0-1.014l3.504-3.412z"/></svg>')
+function register(editor) {
+    editor.ui.registry.addIcon('codeblock', '<svg width="24" height="24"><path d="M4 3h16c.6 0 1 .4 1 1v16c0 .6-.4 1-1 1H4a1 1 0 0 1-1-1V4c0-.6.4-1 1-1Zm1 2v14h14V5Z"/><path d="M11.103 15.423c.277.277.277.738 0 .922a.692.692 0 0 1-1.106 0l-4.057-3.78a.738.738 0 0 1 0-1.107l4.057-3.872c.276-.277.83-.277 1.106 0a.724.724 0 0 1 0 1.014L7.6 12.012ZM12.897 8.577c-.245-.312-.2-.675.08-.955.28-.281.727-.27 1.027.033l4.057 3.78a.738.738 0 0 1 0 1.107l-4.057 3.872c-.277.277-.83.277-1.107 0a.724.724 0 0 1 0-1.014l3.504-3.412z"/></svg>');
 
     editor.ui.registry.addButton('codeeditor', {
         tooltip: 'Insert code block',
         icon: 'codeblock',
         onAction() {
             editor.execCommand('codeeditor');
-        }
+        },
     });
 
     editor.ui.registry.addButton('editcodeeditor', {
@@ -158,7 +162,7 @@ function register(editor, url) {
         icon: 'edit-block',
         onAction() {
             editor.execCommand('codeeditor');
-        }
+        },
     });
 
     editor.addCommand('codeeditor', () => {
@@ -180,17 +184,17 @@ function register(editor, url) {
         }
     });
 
-    editor.on('dblclick', event => {
-        let selectedNode = editor.selection.getNode();
+    editor.on('dblclick', () => {
+        const selectedNode = editor.selection.getNode();
         if (elemIsCodeBlock(selectedNode)) {
             showPopupForCodeBlock(editor, selectedNode);
         }
     });
 
     editor.on('PreInit', () => {
-        editor.parser.addNodeFilter('pre', function(elms) {
+        editor.parser.addNodeFilter('pre', elms => {
             for (const el of elms) {
-                const wrapper = tinymce.html.Node.create('code-block', {
+                const wrapper = window.tinymce.html.Node.create('code-block', {
                     contenteditable: 'false',
                 });
 
@@ -203,13 +207,13 @@ function register(editor, url) {
             }
         });
 
-        editor.parser.addNodeFilter('code-block', function(elms) {
+        editor.parser.addNodeFilter('code-block', elms => {
             for (const el of elms) {
                 el.attr('contenteditable', 'false');
             }
         });
 
-        editor.serializer.addNodeFilter('code-block', function(elms) {
+        editor.serializer.addNodeFilter('code-block', elms => {
             for (const el of elms) {
                 el.unwrap();
             }
@@ -217,12 +221,12 @@ function register(editor, url) {
     });
 
     editor.ui.registry.addContextToolbar('codeeditor', {
-        predicate: function (node) {
+        predicate(node) {
             return node.nodeName.toLowerCase() === 'code-block';
         },
         items: 'editcodeeditor',
         position: 'node',
-        scope: 'node'
+        scope: 'node',
     });
 
     editor.on('PreInit', () => {
@@ -231,9 +235,8 @@ function register(editor, url) {
 }
 
 /**
- * @param {WysiwygConfigOptions} options
  * @return {register}
  */
-export function getPlugin(options) {
+export function getPlugin() {
     return register;
-}
\ No newline at end of file
+}
index 9f4a065adf867561f52e7ddc0e7f79202b13c275..7b1750786eafeb25bd51e9fa161ab1fe2f19c794 100644 (file)
@@ -1,4 +1,4 @@
-import DrawIO from "../services/drawio";
+import * as DrawIO from '../services/drawio';
 
 let pageEditor = null;
 let currentNode = null;
@@ -16,12 +16,12 @@ function showDrawingManager(mceEditor, selectedNode = null) {
     pageEditor = mceEditor;
     currentNode = selectedNode;
 
-    /** @type {ImageManager} **/
+    /** @type {ImageManager} * */
     const imageManager = window.$components.first('image-manager');
-    imageManager.show(function (image) {
+    imageManager.show(image => {
         if (selectedNode) {
             const imgElem = selectedNode.querySelector('img');
-            pageEditor.undoManager.transact(function () {
+            pageEditor.undoManager.transact(() => {
                 pageEditor.dom.setAttrib(imgElem, 'src', image.url);
                 pageEditor.dom.setAttrib(selectedNode, 'drawio-diagram', image.id);
             });
@@ -32,32 +32,26 @@ function showDrawingManager(mceEditor, selectedNode = null) {
     }, 'drawio');
 }
 
-function showDrawingEditor(mceEditor, selectedNode = null) {
-    pageEditor = mceEditor;
-    currentNode = selectedNode;
-    DrawIO.show(options.drawioUrl, drawingInit, updateContent);
-}
-
 async function updateContent(pngData) {
-    const id = "image-" + Math.random().toString(16).slice(2);
+    const id = `image-${Math.random().toString(16).slice(2)}`;
     const loadingImage = window.baseUrl('/loading.gif');
 
-    const handleUploadError = (error) => {
+    const handleUploadError = error => {
         if (error.status === 413) {
             window.$events.emit('error', options.translations.serverUploadLimitText);
         } else {
             window.$events.emit('error', options.translations.imageUploadErrorText);
         }
-        console.log(error);
+        console.error(error);
     };
 
     // Handle updating an existing image
     if (currentNode) {
         DrawIO.close();
-        let imgElem = currentNode.querySelector('img');
+        const imgElem = currentNode.querySelector('img');
         try {
             const img = await DrawIO.upload(pngData, options.pageId);
-            pageEditor.undoManager.transact(function () {
+            pageEditor.undoManager.transact(() => {
                 pageEditor.dom.setAttrib(imgElem, 'src', img.url);
                 pageEditor.dom.setAttrib(currentNode, 'drawio-diagram', img.id);
             });
@@ -72,7 +66,7 @@ async function updateContent(pngData) {
         DrawIO.close();
         try {
             const img = await DrawIO.upload(pngData, options.pageId);
-            pageEditor.undoManager.transact(function () {
+            pageEditor.undoManager.transact(() => {
                 pageEditor.dom.setAttrib(id, 'src', img.url);
                 pageEditor.dom.get(id).parentNode.setAttribute('drawio-diagram', img.id);
             });
@@ -83,7 +77,6 @@ async function updateContent(pngData) {
     }, 5);
 }
 
-
 function drawingInit() {
     if (!currentNode) {
         return Promise.resolve('');
@@ -93,6 +86,66 @@ function drawingInit() {
     return DrawIO.load(drawingId);
 }
 
+function showDrawingEditor(mceEditor, selectedNode = null) {
+    pageEditor = mceEditor;
+    currentNode = selectedNode;
+    DrawIO.show(options.drawioUrl, drawingInit, updateContent);
+}
+
+/**
+ * @param {Editor} editor
+ */
+function register(editor) {
+    editor.addCommand('drawio', () => {
+        const selectedNode = editor.selection.getNode();
+        showDrawingEditor(editor, isDrawing(selectedNode) ? selectedNode : null);
+    });
+
+    editor.ui.registry.addIcon('diagram', `<svg width="24" height="24" fill="${options.darkMode ? '#BBB' : '#000000'}" xmlns="http://www.w3.org/2000/svg"><path d="M20.716 7.639V2.845h-4.794v1.598h-7.99V2.845H3.138v4.794h1.598v7.99H3.138v4.794h4.794v-1.598h7.99v1.598h4.794v-4.794h-1.598v-7.99zM4.736 4.443h1.598V6.04H4.736zm1.598 14.382H4.736v-1.598h1.598zm9.588-1.598h-7.99v-1.598H6.334v-7.99h1.598V6.04h7.99v1.598h1.598v7.99h-1.598zm3.196 1.598H17.52v-1.598h1.598zM17.52 6.04V4.443h1.598V6.04zm-4.21 7.19h-2.79l-.582 1.599H8.643l2.717-7.191h1.119l2.724 7.19h-1.302zm-2.43-1.006h2.086l-1.039-3.06z"/></svg>`);
+
+    editor.ui.registry.addSplitButton('drawio', {
+        tooltip: 'Insert/edit drawing',
+        icon: 'diagram',
+        onAction() {
+            editor.execCommand('drawio');
+            // Hack to de-focus the tinymce editor toolbar
+            window.document.body.dispatchEvent(new Event('mousedown', {bubbles: true}));
+        },
+        fetch(callback) {
+            callback([
+                {
+                    type: 'choiceitem',
+                    text: 'Drawing manager',
+                    value: 'drawing-manager',
+                },
+            ]);
+        },
+        onItemAction(api, value) {
+            if (value === 'drawing-manager') {
+                const selectedNode = editor.selection.getNode();
+                showDrawingManager(editor, isDrawing(selectedNode) ? selectedNode : null);
+            }
+        },
+    });
+
+    editor.on('dblclick', () => {
+        const selectedNode = editor.selection.getNode();
+        if (!isDrawing(selectedNode)) return;
+        showDrawingEditor(editor, selectedNode);
+    });
+
+    editor.on('SetContent', () => {
+        const drawings = editor.dom.select('body > div[drawio-diagram]');
+        if (!drawings.length) return;
+
+        editor.undoManager.transact(() => {
+            for (const drawing of drawings) {
+                drawing.setAttribute('contenteditable', 'false');
+            }
+        });
+    });
+}
+
 /**
  *
  * @param {WysiwygConfigOptions} providedOptions
@@ -100,56 +153,5 @@ function drawingInit() {
  */
 export function getPlugin(providedOptions) {
     options = providedOptions;
-    return function(editor, url) {
-
-        editor.addCommand('drawio', () => {
-            const selectedNode = editor.selection.getNode();
-            showDrawingEditor(editor, isDrawing(selectedNode) ? selectedNode : null);
-        });
-
-        editor.ui.registry.addIcon('diagram', `<svg width="24" height="24" fill="${options.darkMode ? '#BBB' : '#000000'}" xmlns="http://www.w3.org/2000/svg"><path d="M20.716 7.639V2.845h-4.794v1.598h-7.99V2.845H3.138v4.794h1.598v7.99H3.138v4.794h4.794v-1.598h7.99v1.598h4.794v-4.794h-1.598v-7.99zM4.736 4.443h1.598V6.04H4.736zm1.598 14.382H4.736v-1.598h1.598zm9.588-1.598h-7.99v-1.598H6.334v-7.99h1.598V6.04h7.99v1.598h1.598v7.99h-1.598zm3.196 1.598H17.52v-1.598h1.598zM17.52 6.04V4.443h1.598V6.04zm-4.21 7.19h-2.79l-.582 1.599H8.643l2.717-7.191h1.119l2.724 7.19h-1.302zm-2.43-1.006h2.086l-1.039-3.06z"/></svg>`)
-
-        editor.ui.registry.addSplitButton('drawio', {
-            tooltip: 'Insert/edit drawing',
-            icon: 'diagram',
-            onAction() {
-                editor.execCommand('drawio');
-                // Hack to de-focus the tinymce editor toolbar
-                window.document.body.dispatchEvent(new Event('mousedown', {bubbles: true}));
-            },
-            fetch(callback) {
-                callback([
-                    {
-                        type: 'choiceitem',
-                        text: 'Drawing manager',
-                        value: 'drawing-manager',
-                    }
-                ]);
-            },
-            onItemAction(api, value) {
-                if (value === 'drawing-manager') {
-                    const selectedNode = editor.selection.getNode();
-                    showDrawingManager(editor, isDrawing(selectedNode) ? selectedNode : null);
-                }
-            }
-        });
-
-        editor.on('dblclick', event => {
-            let selectedNode = editor.selection.getNode();
-            if (!isDrawing(selectedNode)) return;
-            showDrawingEditor(editor, selectedNode);
-        });
-
-        editor.on('SetContent', function () {
-            const drawings = editor.dom.select('body > div[drawio-diagram]');
-            if (!drawings.length) return;
-
-            editor.undoManager.transact(function () {
-                for (const drawing of drawings) {
-                    drawing.setAttribute('contenteditable', 'false');
-                }
-            });
-        });
-
-    };
-}
\ No newline at end of file
+    return register;
+}
index 1585de72d2023a0a6b6c6059e25443962e5cba1e..096b4f96805a98ba8676ed7c7f95789b096bc014 100644 (file)
@@ -1,9 +1,7 @@
 /**
  * @param {Editor} editor
- * @param {String} url
  */
-function register(editor, url) {
-
+function register(editor) {
     const aboutDialog = {
         title: 'About the WYSIWYG Editor',
         url: window.baseUrl('/help/wysiwyg'),
@@ -13,17 +11,14 @@ function register(editor, url) {
         icon: 'help',
         tooltip: 'About the editor',
         onAction() {
-            tinymce.activeEditor.windowManager.openUrl(aboutDialog);
-        }
+            window.tinymce.activeEditor.windowManager.openUrl(aboutDialog);
+        },
     });
-
 }
 
-
 /**
- * @param {WysiwygConfigOptions} options
  * @return {register}
  */
-export function getPlugin(options) {
+export function getPlugin() {
     return register;
-}
\ No newline at end of file
+}
index df1984d4ee998ae4e61f4f6b95721fee1f5919fb..f5da947f21c151a16cba5922b9586956dd63eb67 100644 (file)
@@ -1,12 +1,11 @@
 /**
  * @param {Editor} editor
- * @param {String} url
  */
-function register(editor, url) {
-    editor.addCommand('InsertHorizontalRule', function () {
-        let hrElem = document.createElement('hr');
-        let cNode = editor.selection.getNode();
-        let parentNode = cNode.parentNode;
+function register(editor) {
+    editor.addCommand('InsertHorizontalRule', () => {
+        const hrElem = document.createElement('hr');
+        const cNode = editor.selection.getNode();
+        const {parentNode} = cNode;
         parentNode.insertBefore(hrElem, cNode);
     });
 
@@ -15,15 +14,13 @@ function register(editor, url) {
         tooltip: 'Insert horizontal line',
         onAction() {
             editor.execCommand('InsertHorizontalRule');
-        }
+        },
     });
 }
 
-
 /**
- * @param {WysiwygConfigOptions} options
  * @return {register}
  */
-export function getPlugin(options) {
+export function getPlugin() {
     return register;
-}
\ No newline at end of file
+}
index 44a0a35ab1228a4ffbe0ee49a263e8595684764a..c4a6d927d2b53646144d0a7650ed12e978505bf0 100644 (file)
@@ -1,102 +1,4 @@
-/**
- * @param {Editor} editor
- * @param {String} url
- */
-import {blockElementTypes} from "./util";
-
-function register(editor, url) {
-
-    editor.ui.registry.addIcon('details', '<svg width="24" height="24"><path d="M8.2 9a.5.5 0 0 0-.4.8l4 5.6a.5.5 0 0 0 .8 0l4-5.6a.5.5 0 0 0-.4-.8ZM20.122 18.151h-16c-.964 0-.934 2.7 0 2.7h16c1.139 0 1.173-2.7 0-2.7zM20.122 3.042h-16c-.964 0-.934 2.7 0 2.7h16c1.139 0 1.173-2.7 0-2.7z"/></svg>');
-    editor.ui.registry.addIcon('togglefold', '<svg height="24"  width="24"><path d="M8.12 19.3c.39.39 1.02.39 1.41 0L12 16.83l2.47 2.47c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41l-3.17-3.17c-.39-.39-1.02-.39-1.41 0l-3.17 3.17c-.4.38-.4 1.02-.01 1.41zm7.76-14.6c-.39-.39-1.02-.39-1.41 0L12 7.17 9.53 4.7c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.03 0 1.42l3.17 3.17c.39.39 1.02.39 1.41 0l3.17-3.17c.4-.39.4-1.03.01-1.42z"/></svg>');
-    editor.ui.registry.addIcon('togglelabel', '<svg height="18" width="18" viewBox="0 0 24 24"><path d="M21.41,11.41l-8.83-8.83C12.21,2.21,11.7,2,11.17,2H4C2.9,2,2,2.9,2,4v7.17c0,0.53,0.21,1.04,0.59,1.41l8.83,8.83 c0.78,0.78,2.05,0.78,2.83,0l7.17-7.17C22.2,13.46,22.2,12.2,21.41,11.41z M6.5,8C5.67,8,5,7.33,5,6.5S5.67,5,6.5,5S8,5.67,8,6.5 S7.33,8,6.5,8z"/></svg>');
-
-    editor.ui.registry.addButton('details', {
-        icon: 'details',
-        tooltip: 'Insert collapsible block',
-        onAction() {
-            editor.execCommand('InsertDetailsBlock');
-        }
-    });
-
-    editor.ui.registry.addButton('removedetails', {
-        icon: 'table-delete-table',
-        tooltip: 'Unwrap',
-        onAction() {
-            unwrapDetailsInSelection(editor)
-        }
-    });
-
-    editor.ui.registry.addButton('editdetials', {
-        icon: 'togglelabel',
-        tooltip: 'Edit label',
-        onAction() {
-            showDetailLabelEditWindow(editor);
-        }
-    });
-
-    editor.on('dblclick', event => {
-        if (!getSelectedDetailsBlock(editor) || event.target.closest('doc-root')) return;
-        showDetailLabelEditWindow(editor);
-    });
-
-    editor.ui.registry.addButton('toggledetails', {
-        icon: 'togglefold',
-        tooltip: 'Toggle open/closed',
-        onAction() {
-            const details = getSelectedDetailsBlock(editor);
-            details.toggleAttribute('open');
-            editor.focus();
-        }
-    });
-
-    editor.addCommand('InsertDetailsBlock', function () {
-        let content = editor.selection.getContent({format: 'html'});
-        const details = document.createElement('details');
-        const summary = document.createElement('summary');
-        const id = 'details-' + Date.now();
-        details.setAttribute('data-id', id)
-        details.appendChild(summary);
-
-        if (!content) {
-            content = '<p><br></p>';
-        }
-
-        details.innerHTML += content;
-        editor.insertContent(details.outerHTML);
-        editor.focus();
-
-        const domDetails = editor.dom.select(`[data-id="${id}"]`)[0] || null;
-        if (domDetails) {
-            const firstChild = domDetails.querySelector('doc-root > *');
-            if (firstChild) {
-                firstChild.focus();
-            }
-            domDetails.removeAttribute('data-id');
-        }
-    });
-
-    editor.ui.registry.addContextToolbar('details', {
-        predicate: function (node) {
-            return node.nodeName.toLowerCase() === 'details';
-        },
-        items: 'editdetials toggledetails removedetails',
-        position: 'node',
-        scope: 'node'
-    });
-
-    editor.on('PreInit', () => {
-        setupElementFilters(editor);
-    });
-}
-
-/**
- * @param {Editor} editor
- */
-function showDetailLabelEditWindow(editor) {
-    const details = getSelectedDetailsBlock(editor);
-    const dialog = editor.windowManager.open(detailsDialog(editor));
-    dialog.setData({summary: getSummaryTextFromDetails(details)});
-}
+import {blockElementTypes} from './util';
 
 /**
  * @param {Editor} editor
@@ -105,15 +7,18 @@ function getSelectedDetailsBlock(editor) {
     return editor.selection.getNode().closest('details');
 }
 
-/**
- * @param {Element} element
- */
-function getSummaryTextFromDetails(element) {
-    const summary = element.querySelector('summary');
-    if (!summary) {
-        return '';
-    }
-    return summary.textContent;
+function setSummary(editor, summaryContent) {
+    const details = getSelectedDetailsBlock(editor);
+    if (!details) return;
+
+    editor.undoManager.transact(() => {
+        let summary = details.querySelector('summary');
+        if (!summary) {
+            summary = document.createElement('summary');
+            details.prepend(summary);
+        }
+        summary.textContent = summaryContent;
+    });
 }
 
 /**
@@ -135,34 +40,40 @@ function detailsDialog(editor) {
         buttons: [
             {
                 type: 'cancel',
-                text: 'Cancel'
+                text: 'Cancel',
             },
             {
                 type: 'submit',
                 text: 'Save',
                 primary: true,
-            }
+            },
         ],
         onSubmit(api) {
             const {summary} = api.getData();
             setSummary(editor, summary);
             api.close();
-        }
+        },
+    };
+}
+
+/**
+ * @param {Element} element
+ */
+function getSummaryTextFromDetails(element) {
+    const summary = element.querySelector('summary');
+    if (!summary) {
+        return '';
     }
+    return summary.textContent;
 }
 
-function setSummary(editor, summaryContent) {
+/**
+ * @param {Editor} editor
+ */
+function showDetailLabelEditWindow(editor) {
     const details = getSelectedDetailsBlock(editor);
-    if (!details) return;
-
-    editor.undoManager.transact(() => {
-        let summary = details.querySelector('summary');
-        if (!summary) {
-            summary = document.createElement('summary');
-            details.prepend(summary);
-        }
-        summary.textContent = summaryContent;
-    });
+    const dialog = editor.windowManager.open(detailsDialog(editor));
+    dialog.setData({summary: getSummaryTextFromDetails(details)});
 }
 
 /**
@@ -188,27 +99,21 @@ function unwrapDetailsInSelection(editor) {
 }
 
 /**
- * @param {Editor} editor
+ * @param {tinymce.html.Node} detailsEl
  */
-function setupElementFilters(editor) {
-    editor.parser.addNodeFilter('details', function(elms) {
-        for (const el of elms) {
-            ensureDetailsWrappedInEditable(el);
-        }
-    });
-
-    editor.serializer.addNodeFilter('details', function(elms) {
-        for (const el of elms) {
-            unwrapDetailsEditable(el);
-            el.attr('open', null);
+function unwrapDetailsEditable(detailsEl) {
+    detailsEl.attr('contenteditable', null);
+    let madeUnwrap = false;
+    for (const child of detailsEl.children()) {
+        if (child.name === 'doc-root') {
+            child.unwrap();
+            madeUnwrap = true;
         }
-    });
+    }
 
-    editor.serializer.addNodeFilter('doc-root', function(elms) {
-        for (const el of elms) {
-            el.unwrap();
-        }
-    });
+    if (madeUnwrap) {
+        unwrapDetailsEditable(detailsEl);
+    }
 }
 
 /**
@@ -218,7 +123,7 @@ function ensureDetailsWrappedInEditable(detailsEl) {
     unwrapDetailsEditable(detailsEl);
 
     detailsEl.attr('contenteditable', 'false');
-    const rootWrap = tinymce.html.Node.create('doc-root', {contenteditable: 'true'});
+    const rootWrap = window.tinymce.html.Node.create('doc-root', {contenteditable: 'true'});
     let previousBlockWrap = null;
 
     for (const child of detailsEl.children()) {
@@ -227,7 +132,7 @@ function ensureDetailsWrappedInEditable(detailsEl) {
 
         if (!isBlock) {
             if (!previousBlockWrap) {
-                previousBlockWrap = tinymce.html.Node.create('p');
+                previousBlockWrap = window.tinymce.html.Node.create('p');
                 rootWrap.append(previousBlockWrap);
             }
             previousBlockWrap.append(child);
@@ -241,28 +146,119 @@ function ensureDetailsWrappedInEditable(detailsEl) {
 }
 
 /**
- * @param {tinymce.html.Node} detailsEl
+ * @param {Editor} editor
  */
-function unwrapDetailsEditable(detailsEl) {
-    detailsEl.attr('contenteditable', null);
-    let madeUnwrap = false;
-    for (const child of detailsEl.children()) {
-        if (child.name === 'doc-root') {
-            child.unwrap();
-            madeUnwrap = true;
+function setupElementFilters(editor) {
+    editor.parser.addNodeFilter('details', elms => {
+        for (const el of elms) {
+            ensureDetailsWrappedInEditable(el);
         }
-    }
+    });
 
-    if (madeUnwrap) {
-        unwrapDetailsEditable(detailsEl);
-    }
+    editor.serializer.addNodeFilter('details', elms => {
+        for (const el of elms) {
+            unwrapDetailsEditable(el);
+            el.attr('open', null);
+        }
+    });
+
+    editor.serializer.addNodeFilter('doc-root', elms => {
+        for (const el of elms) {
+            el.unwrap();
+        }
+    });
 }
 
+/**
+ * @param {Editor} editor
+ */
+function register(editor) {
+    editor.ui.registry.addIcon('details', '<svg width="24" height="24"><path d="M8.2 9a.5.5 0 0 0-.4.8l4 5.6a.5.5 0 0 0 .8 0l4-5.6a.5.5 0 0 0-.4-.8ZM20.122 18.151h-16c-.964 0-.934 2.7 0 2.7h16c1.139 0 1.173-2.7 0-2.7zM20.122 3.042h-16c-.964 0-.934 2.7 0 2.7h16c1.139 0 1.173-2.7 0-2.7z"/></svg>');
+    editor.ui.registry.addIcon('togglefold', '<svg height="24"  width="24"><path d="M8.12 19.3c.39.39 1.02.39 1.41 0L12 16.83l2.47 2.47c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41l-3.17-3.17c-.39-.39-1.02-.39-1.41 0l-3.17 3.17c-.4.38-.4 1.02-.01 1.41zm7.76-14.6c-.39-.39-1.02-.39-1.41 0L12 7.17 9.53 4.7c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.03 0 1.42l3.17 3.17c.39.39 1.02.39 1.41 0l3.17-3.17c.4-.39.4-1.03.01-1.42z"/></svg>');
+    editor.ui.registry.addIcon('togglelabel', '<svg height="18" width="18" viewBox="0 0 24 24"><path d="M21.41,11.41l-8.83-8.83C12.21,2.21,11.7,2,11.17,2H4C2.9,2,2,2.9,2,4v7.17c0,0.53,0.21,1.04,0.59,1.41l8.83,8.83 c0.78,0.78,2.05,0.78,2.83,0l7.17-7.17C22.2,13.46,22.2,12.2,21.41,11.41z M6.5,8C5.67,8,5,7.33,5,6.5S5.67,5,6.5,5S8,5.67,8,6.5 S7.33,8,6.5,8z"/></svg>');
+
+    editor.ui.registry.addButton('details', {
+        icon: 'details',
+        tooltip: 'Insert collapsible block',
+        onAction() {
+            editor.execCommand('InsertDetailsBlock');
+        },
+    });
+
+    editor.ui.registry.addButton('removedetails', {
+        icon: 'table-delete-table',
+        tooltip: 'Unwrap',
+        onAction() {
+            unwrapDetailsInSelection(editor);
+        },
+    });
+
+    editor.ui.registry.addButton('editdetials', {
+        icon: 'togglelabel',
+        tooltip: 'Edit label',
+        onAction() {
+            showDetailLabelEditWindow(editor);
+        },
+    });
+
+    editor.on('dblclick', event => {
+        if (!getSelectedDetailsBlock(editor) || event.target.closest('doc-root')) return;
+        showDetailLabelEditWindow(editor);
+    });
+
+    editor.ui.registry.addButton('toggledetails', {
+        icon: 'togglefold',
+        tooltip: 'Toggle open/closed',
+        onAction() {
+            const details = getSelectedDetailsBlock(editor);
+            details.toggleAttribute('open');
+            editor.focus();
+        },
+    });
+
+    editor.addCommand('InsertDetailsBlock', () => {
+        let content = editor.selection.getContent({format: 'html'});
+        const details = document.createElement('details');
+        const summary = document.createElement('summary');
+        const id = `details-${Date.now()}`;
+        details.setAttribute('data-id', id);
+        details.appendChild(summary);
+
+        if (!content) {
+            content = '<p><br></p>';
+        }
+
+        details.innerHTML += content;
+        editor.insertContent(details.outerHTML);
+        editor.focus();
+
+        const domDetails = editor.dom.select(`[data-id="${id}"]`)[0] || null;
+        if (domDetails) {
+            const firstChild = domDetails.querySelector('doc-root > *');
+            if (firstChild) {
+                firstChild.focus();
+            }
+            domDetails.removeAttribute('data-id');
+        }
+    });
+
+    editor.ui.registry.addContextToolbar('details', {
+        predicate(node) {
+            return node.nodeName.toLowerCase() === 'details';
+        },
+        items: 'editdetials toggledetails removedetails',
+        position: 'node',
+        scope: 'node',
+    });
+
+    editor.on('PreInit', () => {
+        setupElementFilters(editor);
+    });
+}
 
 /**
- * @param {WysiwygConfigOptions} options
  * @return {register}
  */
-export function getPlugin(options) {
+export function getPlugin() {
     return register;
-}
\ No newline at end of file
+}
index 6969a50e22264bb77e96c626dc93d04329edeb3e..37b5bfafd653b7f45fc523fd7b2775cbdd3e0c7c 100644 (file)
@@ -1,32 +1,29 @@
 /**
  * @param {Editor} editor
- * @param {String} url
  */
-function register(editor, url) {
+function register(editor) {
     // Custom Image picker button
     editor.ui.registry.addButton('imagemanager-insert', {
         title: 'Insert image',
         icon: 'image',
         tooltip: 'Insert image',
         onAction() {
-            /** @type {ImageManager} **/
+            /** @type {ImageManager} * */
             const imageManager = window.$components.first('image-manager');
-            imageManager.show(function (image) {
+            imageManager.show(image => {
                 const imageUrl = image.thumbs.display || image.url;
                 let html = `<a href="${image.url}" target="_blank">`;
                 html += `<img src="${imageUrl}" alt="${image.name}">`;
                 html += '</a>';
                 editor.execCommand('mceInsertContent', false, html);
             }, 'gallery');
-        }
+        },
     });
 }
 
-
 /**
- * @param {WysiwygConfigOptions} options
  * @return {register}
  */
-export function getPlugin(options) {
+export function getPlugin() {
     return register;
-}
\ No newline at end of file
+}
index d220ac02da71f241be949049e02e50a1374e09e9..38725a1809e91107cd6f6f4ea2c40e32fb449102 100644 (file)
@@ -6,11 +6,10 @@ function register(editor, url) {
 
 }
 
-
 /**
  * @param {WysiwygConfigOptions} options
  * @return {register}
  */
 export function getPlugin(options) {
     return register;
-}
\ No newline at end of file
+}
index 4afbfa8e623600466cb52538ee577b4686a8745d..191f8364927817c5129858719608c7b31669cc33 100644 (file)
@@ -1,9 +1,82 @@
 /**
+ * @param {Element} element
+ * @return {boolean}
+ */
+function elementWithinTaskList(element) {
+    const listEl = element.closest('li');
+    return listEl && listEl.parentNode.nodeName === 'UL' && listEl.classList.contains('task-list-item');
+}
+
+/**
+ * @param {MouseEvent} event
+ * @param {Element} clickedEl
  * @param {Editor} editor
- * @param {String} url
  */
-function register(editor, url) {
+function handleTaskListItemClick(event, clickedEl, editor) {
+    const bounds = clickedEl.getBoundingClientRect();
+    const withinBounds = event.clientX <= bounds.right
+        && event.clientX >= bounds.left
+        && event.clientY >= bounds.top
+        && event.clientY <= bounds.bottom;
+
+    // Outside of the task list item bounds mean we're probably clicking the pseudo-element.
+    if (!withinBounds) {
+        editor.undoManager.transact(() => {
+            if (clickedEl.hasAttribute('checked')) {
+                clickedEl.removeAttribute('checked');
+            } else {
+                clickedEl.setAttribute('checked', 'checked');
+            }
+        });
+    }
+}
+
+/**
+ * @param {AstNode} node
+ */
+function parseTaskListNode(node) {
+    // Force task list item class
+    node.attr('class', 'task-list-item');
 
+    // Copy checkbox status and remove checkbox within editor
+    for (const child of node.children()) {
+        if (child.name === 'input') {
+            if (child.attr('checked') === 'checked') {
+                node.attr('checked', 'checked');
+            }
+            child.remove();
+        }
+    }
+}
+
+/**
+ * @param {AstNode} node
+ */
+function serializeTaskListNode(node) {
+    // Get checked status and clean it from list node
+    const isChecked = node.attr('checked') === 'checked';
+    node.attr('checked', null);
+
+    const inputAttrs = {type: 'checkbox', disabled: 'disabled'};
+    if (isChecked) {
+        inputAttrs.checked = 'checked';
+    }
+
+    // Create & insert checkbox input element
+    const checkbox = window.tinymce.html.Node.create('input', inputAttrs);
+    checkbox.shortEnded = true;
+
+    if (node.firstChild) {
+        node.insert(checkbox, node.firstChild, true);
+    } else {
+        node.append(checkbox);
+    }
+}
+
+/**
+ * @param {Editor} editor
+ */
+function register(editor) {
     // Tasklist UI buttons
     editor.ui.registry.addIcon('tasklist', '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M22,8c0-0.55-0.45-1-1-1h-7c-0.55,0-1,0.45-1,1s0.45,1,1,1h7C21.55,9,22,8.55,22,8z M13,16c0,0.55,0.45,1,1,1h7 c0.55,0,1-0.45,1-1c0-0.55-0.45-1-1-1h-7C13.45,15,13,15.45,13,16z M10.47,4.63c0.39,0.39,0.39,1.02,0,1.41l-4.23,4.25 c-0.39,0.39-1.02,0.39-1.42,0L2.7,8.16c-0.39-0.39-0.39-1.02,0-1.41c0.39-0.39,1.02-0.39,1.41,0l1.42,1.42l3.54-3.54 C9.45,4.25,10.09,4.25,10.47,4.63z M10.48,12.64c0.39,0.39,0.39,1.02,0,1.41l-4.23,4.25c-0.39,0.39-1.02,0.39-1.42,0L2.7,16.16 c-0.39-0.39-0.39-1.02,0-1.41s1.02-0.39,1.41,0l1.42,1.42l3.54-3.54C9.45,12.25,10.09,12.25,10.48,12.64L10.48,12.64z"/></svg>');
     editor.ui.registry.addToggleButton('tasklist', {
@@ -28,13 +101,13 @@ function register(editor, url) {
                 const inList = parentListEl && parentListEl.classList.contains('task-list-item');
                 api.setActive(Boolean(inList));
             });
-        }
+        },
     });
 
     // Tweak existing bullet list button active state to not be active
     // when we're in a task list.
     const existingBullListButton = editor.ui.registry.getAll().buttons.bullist;
-    existingBullListButton.onSetup = function(api) {
+    existingBullListButton.onSetup = function customBullListOnSetup(api) {
         editor.on('NodeChange', event => {
             const parentList = event.parents.find(el => el.nodeName === 'LI');
             const inTaskList = parentList && parentList.classList.contains('task-list-item');
@@ -42,38 +115,38 @@ function register(editor, url) {
             api.setActive(Boolean(inUlList && !inTaskList));
         });
     };
-    existingBullListButton.onAction = function() {
+    existingBullListButton.onAction = function customBullListOnAction() {
         // Cheeky hack to prevent list toggle action treating tasklists as normal
         // unordered lists which would unwrap the list on toggle from tasklist to bullet list.
         // Instead we quickly jump through an ordered list first if we're within a tasklist.
         if (elementWithinTaskList(editor.selection.getNode())) {
             editor.execCommand('InsertOrderedList', null, {
-                'list-item-attributes': {class: null}
+                'list-item-attributes': {class: null},
             });
         }
 
         editor.execCommand('InsertUnorderedList', null, {
-            'list-item-attributes': {class: null}
+            'list-item-attributes': {class: null},
         });
     };
     // Tweak existing number list to not allow classes on child items
     const existingNumListButton = editor.ui.registry.getAll().buttons.numlist;
-    existingNumListButton.onAction = function() {
+    existingNumListButton.onAction = function customNumListButtonOnAction() {
         editor.execCommand('InsertOrderedList', null, {
-            'list-item-attributes': {class: null}
+            'list-item-attributes': {class: null},
         });
     };
 
     // Setup filters on pre-init
     editor.on('PreInit', () => {
-        editor.parser.addNodeFilter('li', function(nodes) {
+        editor.parser.addNodeFilter('li', nodes => {
             for (const node of nodes) {
                 if (node.attributes.map.class === 'task-list-item') {
                     parseTaskListNode(node);
                 }
             }
         });
-        editor.serializer.addNodeFilter('li', function(nodes) {
+        editor.serializer.addNodeFilter('li', nodes => {
             for (const node of nodes) {
                 if (node.attributes.map.class === 'task-list-item') {
                     serializeTaskListNode(node);
@@ -83,7 +156,7 @@ function register(editor, url) {
     });
 
     // Handle checkbox click in editor
-    editor.on('click', function(event) {
+    editor.on('click', event => {
         const clickedEl = event.target;
         if (clickedEl.nodeName === 'LI' && clickedEl.classList.contains('task-list-item')) {
             handleTaskListItemClick(event, clickedEl, editor);
@@ -93,79 +166,8 @@ function register(editor, url) {
 }
 
 /**
- * @param {Element} element
- * @return {boolean}
- */
-function elementWithinTaskList(element) {
-    const listEl = element.closest('li');
-    return listEl && listEl.parentNode.nodeName === 'UL' && listEl.classList.contains('task-list-item');
-}
-
-/**
- * @param {MouseEvent} event
- * @param {Element} clickedEl
- * @param {Editor} editor
- */
-function handleTaskListItemClick(event, clickedEl, editor) {
-    const bounds = clickedEl.getBoundingClientRect();
-    const withinBounds = event.clientX <= bounds.right
-                        && event.clientX >= bounds.left
-                        && event.clientY >= bounds.top
-                        && event.clientY <= bounds.bottom;
-
-    // Outside of the task list item bounds mean we're probably clicking the pseudo-element.
-    if (!withinBounds) {
-        editor.undoManager.transact(() => {
-            if (clickedEl.hasAttribute('checked')) {
-                clickedEl.removeAttribute('checked');
-            }  else {
-                clickedEl.setAttribute('checked', 'checked');
-            }
-        });
-    }
-}
-
-/**
- * @param {AstNode} node
- */
-function parseTaskListNode(node) {
-    // Force task list item class
-    node.attr('class', 'task-list-item');
-
-    // Copy checkbox status and remove checkbox within editor
-    for (const child of node.children()) {
-        if (child.name === 'input') {
-            if (child.attr('checked') === 'checked') {
-                node.attr('checked', 'checked');
-            }
-            child.remove();
-        }
-    }
-}
-
-/**
- * @param {AstNode} node
- */
-function serializeTaskListNode(node) {
-    // Get checked status and clean it from list node
-    const isChecked = node.attr('checked') === 'checked';
-    node.attr('checked', null);
-
-    const inputAttrs = {type: 'checkbox', disabled: 'disabled'};
-    if (isChecked) {
-        inputAttrs.checked = 'checked';
-    }
-
-    // Create & insert checkbox input element
-    const checkbox = tinymce.html.Node.create('input', inputAttrs);
-    checkbox.shortEnded = true;
-    node.firstChild ? node.insert(checkbox, node.firstChild, true) : node.append(checkbox);
-}
-
-/**
- * @param {WysiwygConfigOptions} options
  * @return {register}
  */
-export function getPlugin(options) {
+export function getPlugin() {
     return register;
-}
\ No newline at end of file
+}
index f14ef4c643abe778c24e96ca0974aa8e4265bfb0..92f8f158323ca5b1c3a0ea5b828fb937dae1964b 100644 (file)
@@ -1,16 +1,3 @@
-/**
- * Scroll to a section dictated by the current URL query string, if present.
- * Used when directly editing a specific section of the page.
- * @param {Editor} editor
- */
-export function scrollToQueryString(editor) {
-    const queryParams = (new URL(window.location)).searchParams;
-    const scrollId = queryParams.get('content-id');
-    if (scrollId) {
-        scrollToText(editor, scrollId);
-    }
-}
-
 /**
  * @param {Editor} editor
  * @param {String} scrollId
@@ -26,4 +13,17 @@ function scrollToText(editor, scrollId) {
     editor.selection.select(element, true);
     editor.selection.collapse(false);
     editor.focus();
-}
\ No newline at end of file
+}
+
+/**
+ * Scroll to a section dictated by the current URL query string, if present.
+ * Used when directly editing a specific section of the page.
+ * @param {Editor} editor
+ */
+export function scrollToQueryString(editor) {
+    const queryParams = (new URL(window.location)).searchParams;
+    const scrollId = queryParams.get('content-id');
+    if (scrollId) {
+        scrollToText(editor, scrollId);
+    }
+}
index ef364ddadab16b1e95105e7218c4b742cb91a6e4..1c20df9c5516c8df6a640151b0b5be4c1b99fa11 100644 (file)
@@ -4,7 +4,7 @@
 export function register(editor) {
     // Headers
     for (let i = 1; i < 5; i++) {
-        editor.shortcuts.add('meta+' + i, '', ['FormatBlock', false, 'h' + (i+1)]);
+        editor.shortcuts.add(`meta+${i}`, '', ['FormatBlock', false, `h${i + 1}`]);
     }
 
     // Other block shortcuts
@@ -30,24 +30,25 @@ export function register(editor) {
     });
 
     // Loop through callout styles
-    editor.shortcuts.add('meta+9', '', function() {
+    editor.shortcuts.add('meta+9', '', () => {
         const selectedNode = editor.selection.getNode();
         const callout = selectedNode ? selectedNode.closest('.callout') : null;
 
         const formats = ['info', 'success', 'warning', 'danger'];
-        const currentFormatIndex = formats.findIndex(format => callout && callout.classList.contains(format));
+        const currentFormatIndex = formats.findIndex(format => {
+            return callout && callout.classList.contains(format);
+        });
         const newFormatIndex = (currentFormatIndex + 1) % formats.length;
         const newFormat = formats[newFormatIndex];
 
-        editor.formatter.apply('callout' + newFormat);
+        editor.formatter.apply(`callout${newFormat}`);
     });
 
     // Link selector shortcut
-    editor.shortcuts.add('meta+shift+K', '', function() {
-        /** @var {EntitySelectorPopup} **/
+    editor.shortcuts.add('meta+shift+K', '', () => {
+        /** @var {EntitySelectorPopup} * */
         const selectorPopup = window.$components.first('entity-selector-popup');
-        selectorPopup.show(function(entity) {
-
+        selectorPopup.show(entity => {
             if (editor.selection.isCollapsed()) {
                 editor.insertContent(editor.dom.createHTML('a', {href: entity.link}, editor.dom.encode(entity.name)));
             } else {
@@ -56,6 +57,6 @@ export function register(editor) {
 
             editor.selection.collapse(false);
             editor.focus();
-        })
+        });
     });
-}
\ No newline at end of file
+}
index 9debb08b5c4a5d9049ee37638764f5dd8dc1f90a..4663ad132e13f05457c4d6db1697145f7024222d 100644 (file)
@@ -13,7 +13,7 @@ export function getPrimaryToolbar(options) {
         'bullist numlist listoverflow',
         textDirPlugins,
         'link table imagemanager-insert insertoverflow',
-        'code about fullscreen'
+        'code about fullscreen',
     ];
 
     return toolbar.filter(row => Boolean(row)).join(' | ');
@@ -26,17 +26,17 @@ function registerPrimaryToolbarGroups(editor) {
     editor.ui.registry.addGroupToolbarButton('formatoverflow', {
         icon: 'more-drawer',
         tooltip: 'More',
-        items: 'strikethrough superscript subscript inlinecode removeformat'
+        items: 'strikethrough superscript subscript inlinecode removeformat',
     });
     editor.ui.registry.addGroupToolbarButton('listoverflow', {
         icon: 'more-drawer',
         tooltip: 'More',
-        items: 'tasklist outdent indent'
+        items: 'tasklist outdent indent',
     });
     editor.ui.registry.addGroupToolbarButton('insertoverflow', {
         icon: 'more-drawer',
         tooltip: 'More',
-        items: 'customhr codeeditor drawio media details'
+        items: 'customhr codeeditor drawio media details',
     });
 }
 
@@ -50,7 +50,7 @@ function registerLinkContextToolbar(editor) {
         },
         position: 'node',
         scope: 'node',
-        items: 'link unlink openlink'
+        items: 'link unlink openlink',
     });
 }
 
@@ -64,16 +64,15 @@ function registerImageContextToolbar(editor) {
         },
         position: 'node',
         scope: 'node',
-        items: 'image'
+        items: 'image',
     });
 }
 
 /**
  * @param {Editor} editor
- * @param {WysiwygConfigOptions} options
  */
-export function registerAdditionalToolbars(editor, options) {
+export function registerAdditionalToolbars(editor) {
     registerPrimaryToolbarGroups(editor);
     registerLinkContextToolbar(editor);
     registerImageContextToolbar(editor);
-}
\ No newline at end of file
+}
index 1f63b6529cbf4524cdfa9c3342bb31a763b0af44..68b6aabfce54bc4c632629ac876db60fa2d1df82 100644 (file)
@@ -1,5 +1,3 @@
-
-
 export const blockElementTypes = [
     'p',
     'h1',
@@ -15,5 +13,5 @@ export const blockElementTypes = [
     'details',
     'ul',
     'ol',
-    'table'
-];
\ No newline at end of file
+    'table',
+];
index 330923d4fd18bd12f055b9ab2d39e5931e8214f9..0fd347cf893a3bc37f1f5379e849aa6140f552a9 100644 (file)
-/* BASICS */
-
-.CodeMirror {
-  /* Set height, width, borders, and global font properties here */
-  font-family: monospace;
-  height: 300px;
-  color: black;
-  direction: ltr;
-}
-
-/* PADDING */
-
-.CodeMirror-lines {
-  padding: 4px 0; /* Vertical padding around content */
-}
-.CodeMirror pre.CodeMirror-line,
-.CodeMirror pre.CodeMirror-line-like {
-  padding: 0 4px; /* Horizontal padding of content */
-}
-
-.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
-  background-color: white; /* The little square between H and V scrollbars */
-}
-
-/* GUTTER */
-
-.CodeMirror-gutters {
-  border-right: 1px solid #ddd;
-  background-color: #f7f7f7;
-  white-space: nowrap;
-}
-.CodeMirror-linenumbers {}
-.CodeMirror-linenumber {
-  padding: 0 3px 0 5px;
-  min-width: 20px;
-  text-align: right;
-  color: #999;
-  white-space: nowrap;
-}
-
-.CodeMirror-guttermarker { color: black; }
-.CodeMirror-guttermarker-subtle { color: #999; }
-
-/* CURSOR */
-
-.CodeMirror-cursor {
-  border-left: 1px solid black;
-  border-right: none;
-  width: 0;
-}
-/* Shown when moving in bi-directional text */
-.CodeMirror div.CodeMirror-secondarycursor {
-  border-left: 1px solid silver;
-}
-.cm-fat-cursor .CodeMirror-cursor {
-  width: auto;
-  border: 0 !important;
-  background: #7e7;
-}
-.cm-fat-cursor div.CodeMirror-cursors {
-  z-index: 1;
-}
-.cm-fat-cursor-mark {
-  background-color: rgba(20, 255, 20, 0.5);
-  -webkit-animation: blink 1.06s steps(1) infinite;
-  -moz-animation: blink 1.06s steps(1) infinite;
-  animation: blink 1.06s steps(1) infinite;
-}
-.cm-animate-fat-cursor {
-  width: auto;
-  border: 0;
-  -webkit-animation: blink 1.06s steps(1) infinite;
-  -moz-animation: blink 1.06s steps(1) infinite;
-  animation: blink 1.06s steps(1) infinite;
-  background-color: #7e7;
-}
-@-moz-keyframes blink {
-  0% {}
-  50% { background-color: transparent; }
-  100% {}
-}
-@-webkit-keyframes blink {
-  0% {}
-  50% { background-color: transparent; }
-  100% {}
-}
-@keyframes blink {
-  0% {}
-  50% { background-color: transparent; }
-  100% {}
-}
-
-/* Can style cursor different in overwrite (non-insert) mode */
-.CodeMirror-overwrite .CodeMirror-cursor {}
-
-.cm-tab { display: inline-block; text-decoration: inherit; }
-
-.CodeMirror-rulers {
-  position: absolute;
-  left: 0; right: 0; top: -50px; bottom: 0;
-  overflow: hidden;
-}
-.CodeMirror-ruler {
-  border-left: 1px solid #ccc;
-  top: 0; bottom: 0;
-  position: absolute;
-}
-
-/* DEFAULT THEME */
-
-.cm-s-default .cm-header {color: blue;}
-.cm-s-default .cm-quote {color: #090;}
-.cm-negative {color: #d44;}
-.cm-positive {color: #292;}
-.cm-header, .cm-strong {font-weight: bold;}
-.cm-em {font-style: italic;}
-.cm-link {text-decoration: underline;}
-.cm-strikethrough {text-decoration: line-through;}
-
-.cm-s-default .cm-keyword {color: #708;}
-.cm-s-default .cm-atom {color: #219;}
-.cm-s-default .cm-number {color: #164;}
-.cm-s-default .cm-def {color: #00f;}
-.cm-s-default .cm-variable,
-.cm-s-default .cm-punctuation,
-.cm-s-default .cm-property,
-.cm-s-default .cm-operator {}
-.cm-s-default .cm-variable-2 {color: #05a;}
-.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
-.cm-s-default .cm-comment {color: #a50;}
-.cm-s-default .cm-string {color: #a11;}
-.cm-s-default .cm-string-2 {color: #f50;}
-.cm-s-default .cm-meta {color: #555;}
-.cm-s-default .cm-qualifier {color: #555;}
-.cm-s-default .cm-builtin {color: #30a;}
-.cm-s-default .cm-bracket {color: #997;}
-.cm-s-default .cm-tag {color: #170;}
-.cm-s-default .cm-attribute {color: #00c;}
-.cm-s-default .cm-hr {color: #999;}
-.cm-s-default .cm-link {color: #00c;}
-
-.cm-s-default .cm-error {color: #f00;}
-.cm-invalidchar {color: #f00;}
-
-.CodeMirror-composing { border-bottom: 2px solid; }
-
-/* Default styles for common addons */
-
-div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}
-div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
-.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
-.CodeMirror-activeline-background {background: #e8f2ff;}
-
-/* STOP */
-
-/* The rest of this file contains styles related to the mechanics of
-   the editor. You probably shouldn't touch them. */
-
-.CodeMirror {
-  position: relative;
-  overflow: hidden;
-  background: white;
-}
-
-.CodeMirror-scroll {
-  overflow: scroll !important; /* Things will break if this is overridden */
-  /* 50px is the magic margin used to hide the element's real scrollbars */
-  /* See overflow: hidden in .CodeMirror */
-  margin-bottom: -50px; margin-right: -50px;
-  padding-bottom: 50px;
-  height: 100%;
-  outline: none; /* Prevent dragging from highlighting the element */
-  position: relative;
-}
-.CodeMirror-sizer {
-  position: relative;
-  border-right: 50px solid transparent;
-}
-
-/* The fake, visible scrollbars. Used to force redraw during scrolling
-   before actual scrolling happens, thus preventing shaking and
-   flickering artifacts. */
-.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
-  position: absolute;
-  z-index: 6;
-  display: none;
-  outline: none;
-}
-.CodeMirror-vscrollbar {
-  right: 0; top: 0;
-  overflow-x: hidden;
-  overflow-y: scroll;
-}
-.CodeMirror-hscrollbar {
-  bottom: 0; left: 0;
-  overflow-y: hidden;
-  overflow-x: scroll;
-}
-.CodeMirror-scrollbar-filler {
-  right: 0; bottom: 0;
-}
-.CodeMirror-gutter-filler {
-  left: 0; bottom: 0;
-}
-
-.CodeMirror-gutters {
-  position: absolute; left: 0; top: 0;
-  min-height: 100%;
-  z-index: 3;
-}
-.CodeMirror-gutter {
-  white-space: normal;
-  height: 100%;
-  display: inline-block;
-  vertical-align: top;
-  margin-bottom: -50px;
-}
-.CodeMirror-gutter-wrapper {
-  position: absolute;
-  z-index: 4;
-  background: none !important;
-  border: none !important;
-}
-.CodeMirror-gutter-background {
-  position: absolute;
-  top: 0; bottom: 0;
-  z-index: 4;
-}
-.CodeMirror-gutter-elt {
-  position: absolute;
-  cursor: default;
-  z-index: 4;
-}
-.CodeMirror-gutter-wrapper ::selection { background-color: transparent }
-.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
-
-.CodeMirror-lines {
-  cursor: text;
-  min-height: 1px; /* prevents collapsing before first draw */
-}
-.CodeMirror pre.CodeMirror-line,
-.CodeMirror pre.CodeMirror-line-like {
-  /* Reset some styles that the rest of the page might have set */
-  -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
-  border-width: 0;
-  background: transparent;
-  font-family: inherit;
-  font-size: inherit;
-  margin: 0;
-  white-space: pre;
-  word-wrap: normal;
-  line-height: inherit;
-  color: inherit;
-  z-index: 2;
-  position: relative;
-  overflow: visible;
-  -webkit-tap-highlight-color: transparent;
-  -webkit-font-variant-ligatures: contextual;
-  font-variant-ligatures: contextual;
-}
-.CodeMirror-wrap pre.CodeMirror-line,
-.CodeMirror-wrap pre.CodeMirror-line-like {
-  word-wrap: break-word;
-  white-space: pre-wrap;
-  word-break: normal;
-}
-
-.CodeMirror-linebackground {
-  position: absolute;
-  left: 0; right: 0; top: 0; bottom: 0;
-  z-index: 0;
-}
-
-.CodeMirror-linewidget {
-  position: relative;
-  z-index: 2;
-  padding: 0.1px; /* Force widget margins to stay inside of the container */
-}
-
-.CodeMirror-widget {}
-
-.CodeMirror-rtl pre { direction: rtl; }
-
-.CodeMirror-code {
-  outline: none;
-}
-
-/* Force content-box sizing for the elements where we expect it */
-.CodeMirror-scroll,
-.CodeMirror-sizer,
-.CodeMirror-gutter,
-.CodeMirror-gutters,
-.CodeMirror-linenumber {
-  -moz-box-sizing: content-box;
-  box-sizing: content-box;
-}
-
-.CodeMirror-measure {
-  position: absolute;
-  width: 100%;
-  height: 0;
-  overflow: hidden;
-  visibility: hidden;
-}
-
-.CodeMirror-cursor {
-  position: absolute;
-  pointer-events: none;
-}
-.CodeMirror-measure pre { position: static; }
-
-div.CodeMirror-cursors {
-  visibility: hidden;
-  position: relative;
-  z-index: 3;
-}
-div.CodeMirror-dragcursors {
-  visibility: visible;
-}
-
-.CodeMirror-focused div.CodeMirror-cursors {
-  visibility: visible;
-}
-
-.CodeMirror-selected { background: #d9d9d9; }
-.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
-.CodeMirror-crosshair { cursor: crosshair; }
-.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
-.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
-
-.cm-searching {
-  background-color: #ffa;
-  background-color: rgba(255, 255, 0, .4);
-}
-
-/* Used to force a border model for a node */
-.cm-force-border { padding-right: .1px; }
-
-@media print {
-  /* Hide the cursor when printing */
-  .CodeMirror div.CodeMirror-cursors {
-    visibility: hidden;
-  }
-}
-
-/* See issue #2901 */
-.cm-tab-wrap-hack:after { content: ''; }
-
-/* Help users use markselection to safely style text background */
-span.CodeMirror-selectedtext { background: none; }
-
-/* STOP */
-
-/**
- * Codemirror Darcula theme
- */
-
 /**
-    Name: IntelliJ IDEA darcula theme
-    From IntelliJ IDEA by JetBrains
+ * Custom CodeMirror BookStack overrides
  */
 
-.cm-s-darcula  { font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif;}
-.cm-s-darcula.CodeMirror { background: #2B2B2B; color: #A9B7C6; }
-
-.cm-s-darcula span.cm-meta { color: #BBB529; }
-.cm-s-darcula span.cm-number { color: #6897BB; }
-.cm-s-darcula span.cm-keyword { color: #CC7832; line-height: 1em; font-weight: bold; }
-.cm-s-darcula span.cm-def { color: #A9B7C6; font-style: italic; }
-.cm-s-darcula span.cm-variable { color: #A9B7C6; }
-.cm-s-darcula span.cm-variable-2 { color: #A9B7C6; }
-.cm-s-darcula span.cm-variable-3 { color: #9876AA; }
-.cm-s-darcula span.cm-type { color: #AABBCC; font-weight: bold; }
-.cm-s-darcula span.cm-property { color: #FFC66D; }
-.cm-s-darcula span.cm-operator { color: #A9B7C6; }
-.cm-s-darcula span.cm-string { color: #6A8759; }
-.cm-s-darcula span.cm-string-2 { color: #6A8759; }
-.cm-s-darcula span.cm-comment { color: #61A151; font-style: italic; }
-.cm-s-darcula span.cm-link { color: #CC7832; }
-.cm-s-darcula span.cm-atom { color: #CC7832; }
-.cm-s-darcula span.cm-error { color: #BC3F3C; }
-.cm-s-darcula span.cm-tag { color: #629755; font-weight: bold; font-style: italic; text-decoration: underline; }
-.cm-s-darcula span.cm-attribute { color: #6897bb; }
-.cm-s-darcula span.cm-qualifier { color: #6A8759; }
-.cm-s-darcula span.cm-bracket { color: #A9B7C6; }
-.cm-s-darcula span.cm-builtin { color: #FF9E59; }
-.cm-s-darcula span.cm-special { color: #FF9E59; }
-.cm-s-darcula span.cm-matchhighlight { color: #FFFFFF; background-color: rgba(50, 89, 48, .7); font-weight: normal;}
-.cm-s-darcula span.cm-searching { color: #FFFFFF; background-color: rgba(61, 115, 59, .7); font-weight: normal;}
-
-.cm-s-darcula .CodeMirror-cursor { border-left: 1px solid #A9B7C6; }
-.cm-s-darcula .CodeMirror-activeline-background { background: #323232; }
-.cm-s-darcula .CodeMirror-gutters { background: #313335; border-right: 1px solid #313335; }
-.cm-s-darcula .CodeMirror-guttermarker { color: #FFEE80; }
-.cm-s-darcula .CodeMirror-guttermarker-subtle { color: #D0D0D0; }
-.cm-s-darcula .CodeMirrir-linenumber { color: #606366; }
-.cm-s-darcula .CodeMirror-matchingbracket { background-color: #3B514D; color: #FFEF28 !important; font-weight: bold; }
-
-.cm-s-darcula div.CodeMirror-selected { background: #214283; }
-
-.CodeMirror-hints.darcula {
-  font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
-  color: #9C9E9E;
-  background-color: #3B3E3F !important;
-}
-
-.CodeMirror-hints.darcula .CodeMirror-hint-active {
-  background-color: #494D4E !important;
-  color: #9C9E9E !important;
-}
-
-/**
- * Custom BookStack overrides
- */
-.CodeMirror, .CodeMirror pre {
+.cm-editor {
   font-size: 12px;
-}
-.CodeMirror {
-  font-size: 12px;
-  height: auto;
+  border: 1px solid #ddd;
+  line-height: 1.4;
   margin-bottom: $-l;
-  border: 1px solid;
-  @include lightDark(border-color, #DDD, #111);
-}
-.CodeMirror pre::after {
-  display: none;
-}
-html.dark-mode .CodeMirror pre {
-  background-color: transparent;
 }
 
-.cm-s-mdn-like .CodeMirror-gutters { background: #f8f8f8; border-left: 0; color: #333; }
+.page-content .cm-editor,
+.CodeMirrorContainer .cm-editor {
+  border-radius: 4px;
+}
 
-.code-fill .CodeMirror {
-  position: absolute;
-  top: 0;
-  bottom: 0;
-  left: 0;
-  width: 100%;
-  height: 100%;
-  margin-bottom: 0;
-  border: 0;
+// Manual dark-mode definition so that it applies to code blocks within the shadow
+// dom which are used within the WYSIWYG editor, as the .dark-mode on the parent
+// <html> node are not applies so instead we have the class on the parent element.
+.dark-mode .cm-editor {
+  border-color: #444;
 }
 
 /**
  * Custom Copy Button
  */
-.CodeMirror-copy {
+.cm-copy-button {
   position: absolute;
+  display: flex;
+  align-items: center;
+  justify-content: center;
   top: -1px;
   right: -1px;
   background-color: #EEE;
   border: 1px solid #DDD;
+  border-radius: 0 4px 0 0;
   @include lightDark(background-color, #eee, #333);
   @include lightDark(border-color, #ddd, #444);
-  @include lightDark(fill, #444, #888);
-  padding: $-xs;
+  @include lightDark(color, #444, #888);
   line-height: 0;
   cursor: pointer;
   z-index: 5;
   user-select: none;
   opacity: 0;
   pointer-events: none;
+  width: 32px;
+  height: 32px;
+  transition: background-color linear 60ms, color linear 60ms;
   svg {
-    transition: all ease-in 240ms;
-    transform: translateY(0);
+    fill: currentColor;
   }
   &.success {
-    background-color: lighten($positive, 10%);
-    svg {
-      fill: #FFF;
-      transform: translateY(-3px);
-    }
+    background: $positive;
+    color: #FFF;
+  }
+  &:focus {
+    outline: 0 !important;
   }
 }
-.CodeMirror:hover .CodeMirror-copy {
+.cm-editor:hover .cm-copy-button  {
   user-select: all;
-  opacity: 1;
+  opacity: .6;
   pointer-events: all;
 }
\ No newline at end of file
index 825501364664abbcc7bddd09e666acd1c2ca0525..4e6a8d731f7aa2058456f973e974c434b6d4e9ad 100644 (file)
@@ -730,7 +730,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
 .code-editor-main {
   flex: 1;
   min-width: 0;
-  .CodeMirror {
+  .cm-editor {
     margin-bottom: 0;
     z-index: 1;
     max-width: 100%;
index b7fc52f7dbdc38793505469653c61293359bdbf8..37f8f1bfcf720d28568eb522033a845fc27629ab 100644 (file)
@@ -75,6 +75,7 @@
   @include lightDark(border-color, #ddd, #000);
   position: relative;
   flex: 1;
+  min-width: 0;
 }
 .markdown-editor-wrap + .markdown-editor-wrap {
   flex-basis: 50%;
   flex-grow: 0;
 }
 
+.markdown-editor-wrap .cm-editor {
+  flex: 1;
+  max-width: 100%;
+  border: 0;
+  margin: 0;
+}
+
 .markdown-panel-divider {
   width: 2px;
   @include lightDark(background-color, #ddd, #000);
index 57718e647167417cf89689fbcabde43af9b42c5d..d58be2fe3504bc027d79cb98696af5ff58764509 100755 (executable)
@@ -178,6 +178,10 @@ body.tox-fullscreen, body.markdown-fullscreen {
       white-space: pre-wrap;
     }
   }
+
+  .cm-editor {
+    margin-bottom: 1.375em;
+  }
 }
 
 // Page content pointers
index edf8ce6144c9d7e2171adf25faf5a8668c985e56..6745d2a546e52da6b6300494b54862e58c11eacc 100644 (file)
@@ -178,18 +178,19 @@ sub, .subscript {
 pre {
   font-size: 12px;
   border: 1px solid #DDD;
-  @include lightDark(background-color, #f5f5f5, #2B2B2B);
+  @include lightDark(background-color, #FFF, #2B2B2B);
   @include lightDark(border-color, #DDD, #111);
-  padding-left: 31px;
+  border-radius: 4px;
+  padding-left: 26px;
   position: relative;
   padding-top: 3px;
   padding-bottom: 3px;
-  &:after {
+  &:before {
     content: '';
     display: block;
     position: absolute;
     top: 0;
-    width: 29px;
+    width: 22.4px;
     left: 0;
     height: 100%;
     @include lightDark(background-color, #f5f5f5, #313335);
index 9c8f927611a95924fef882e705b6f63fb69bf45b..8700a4ccb763ff25dc86ce20b3270bfb6b451d52 100644 (file)
@@ -23,7 +23,7 @@
                     <div refs="code-editor@language-options-container" class="lang-options">
                         @php
                             $languages = [
-                                'Bash', 'CSS', 'C', 'C++', 'C#', 'Dart', 'Diff', 'Fortran', 'F#', 'Go', 'Haskell', 'HTML', 'INI',
+                                'Bash', 'CSS', 'C', 'C++', 'C#', 'Clojure', 'Dart', 'Diff', 'Fortran', 'F#', 'Go', 'Haskell', 'HTML', 'INI',
                                 'Java', 'JavaScript', 'JSON', 'Julia', 'Kotlin', 'LaTeX', 'Lua', 'MarkDown', 'MATLAB', 'MSSQL', 'MySQL', 'Nginx', 'OCaml',
                                 'Octave', 'Pascal', 'Perl', 'PHP', 'PL/SQL', 'PostgreSQL', 'Powershell', 'Python', 'Ruby', 'Rust', 'Scheme', 'Shell', 'Smarty',
                                  'SQL', 'SQLite', 'Swift', 'Twig', 'TypeScript', 'VBScript', 'VB.NET', 'XML', 'YAML',
index fd8a20a0484f68722ad026f6dcf1245187b5fdb4..c488f0e115c8b9eb49120735fd898583440f1866 100644 (file)
@@ -30,7 +30,7 @@
             </div>
         </div>
 
-        <div markdown-input class="flex flex-fill">
+        <div class="flex flex-fill" dir="ltr">
             <textarea id="markdown-editor-input"
                       refs="markdown-editor@input"
                       @if($errors->has('markdown')) class="text-neg" @endif
index b8fe62fa45cee2317d9d759b89e693d8e41aeda7..5bafa6e153fffea70da598c60e53b212e382c064 100644 (file)
@@ -5,8 +5,8 @@
     <div class="pointer anim {{ userCan('page-update', $page) ? 'is-page-editable' : ''}}" >
         <span class="icon mr-xxs">@icon('link') @icon('include', ['style' => 'display:none;'])</span>
         <div class="input-group inline block">
-            <input readonly="readonly" type="text" id="pointer-url" placeholder="url">
-            <button class="button outline icon" data-clipboard-target="#pointer-url" type="button" title="{{ trans('entities.pages_copy_link') }}">@icon('copy')</button>
+            <input refs="pointer@input" readonly="readonly" type="text" id="pointer-url" placeholder="url">
+            <button refs="pointer@button" class="button outline icon" data-clipboard-target="#pointer-url" type="button" title="{{ trans('entities.pages_copy_link') }}">@icon('copy')</button>
         </div>
         @if(userCan('page-update', $page))
             <a href="{{ $page->getUrl('/edit') }}" id="pointer-edit" data-edit-href="{{ $page->getUrl('/edit') }}"
index b18c182ebde971b6016d47836d333ad0a2b8fc6f..b2f08b6412a64f7a623280fa1c967c670d4c50dc 100644 (file)
@@ -5,40 +5,40 @@
 
         @include('settings.parts.navbar', ['selected' => 'users'])
 
-        <div class="card content-wrap auto-height">
-            <h1 class="list-heading">{{ trans('settings.users_delete') }}</h1>
+        <form action="{{ url("/settings/users/{$user->id}") }}" method="POST">
+            {!! csrf_field() !!}
 
-            <p>{{ trans('settings.users_delete_warning', ['userName' => $user->name]) }}</p>
+            <div class="card content-wrap auto-height">
+                <h1 class="list-heading">{{ trans('settings.users_delete') }}</h1>
 
-            @if(userCan('users-manage'))
-                <hr class="my-l">
+                <p>{{ trans('settings.users_delete_warning', ['userName' => $user->name]) }}</p>
 
-                <div class="grid half gap-xl v-center">
-                    <div>
-                        <label class="setting-list-label">{{ trans('settings.users_migrate_ownership') }}</label>
-                        <p class="small">{{ trans('settings.users_migrate_ownership_desc') }}</p>
-                    </div>
-                    <div>
-                        @include('form.user-select', ['name' => 'new_owner_id', 'user' => null])
-                    </div>
-                </div>
-            @endif
+                @if(userCan('users-manage'))
+                    <hr class="my-l">
 
-            <hr class="my-l">
+                    <div class="grid half gap-xl v-center">
+                        <div>
+                            <label class="setting-list-label">{{ trans('settings.users_migrate_ownership') }}</label>
+                            <p class="small">{{ trans('settings.users_migrate_ownership_desc') }}</p>
+                        </div>
+                        <div>
+                            @include('form.user-select', ['name' => 'new_owner_id', 'user' => null])
+                        </div>
+                    </div>
+                @endif
 
-            <div class="grid half">
-                <p class="text-neg"><strong>{{ trans('settings.users_delete_confirm') }}</strong></p>
-                <div>
-                    <form action="{{ url("/settings/users/{$user->id}") }}" method="POST" class="text-right">
-                        {!! csrf_field() !!}
+                <hr class="my-l">
 
+                <div class="grid half">
+                    <p class="text-neg"><strong>{{ trans('settings.users_delete_confirm') }}</strong></p>
+                    <div class="text-right">
                         <input type="hidden" name="_method" value="DELETE">
                         <a href="{{ url("/settings/users/{$user->id}") }}" class="button outline">{{ trans('common.cancel') }}</a>
                         <button type="submit" class="button">{{ trans('common.confirm') }}</button>
-                    </form>
+                    </div>
                 </div>
-            </div>
 
-        </div>
+            </div>
+        </form>
     </div>
 @stop
index d4d975dbd234e47046daa0fd19a8678617c343b4..971479e28aee66d8ed31a33e7c24a71b963d330e 100644 (file)
@@ -163,6 +163,22 @@ class RolesTest extends TestCase
         $this->assertEquals($this->user->id, $roleA->users()->first()->id);
     }
 
+    public function test_delete_with_empty_migrate_option_works()
+    {
+        $role = $this->users->attachNewRole($this->user);
+
+        $this->assertCount(1, $role->users()->get());
+
+        $deletePage = $this->asAdmin()->get("/settings/roles/delete/$role->id");
+        $this->withHtml($deletePage)->assertElementExists('select[name=migrate_role_id]');
+        $resp = $this->asAdmin()->delete("/settings/roles/delete/$role->id", [
+            'migrate_role_id' => '',
+        ]);
+
+        $resp->assertRedirect('/settings/roles');
+        $this->assertDatabaseMissing('roles', ['id' => $role->id]);
+    }
+
     public function test_entity_permissions_are_removed_on_delete()
     {
         /** @var Role $roleA */
index af17db52b525086d9be73ee577651f09712f13e7..7ccc5b773a7e88a120c4ee214a11c5c44fa44ea1 100644 (file)
@@ -145,6 +145,7 @@ class UserManagementTest extends TestCase
 
         $resp = $this->asEditor()->get("settings/users/{$editor->id}/delete");
         $resp->assertSee('Migrate Ownership');
+        $this->withHtml($resp)->assertElementExists('form input[name="new_owner_id"]');
         $resp->assertSee('new_owner_id');
     }
 
@@ -161,6 +162,16 @@ class UserManagementTest extends TestCase
         ]);
     }
 
+    public function test_delete_with_empty_owner_migration_id_works()
+    {
+        $user = $this->users->editor();
+
+        $resp = $this->asAdmin()->delete("settings/users/{$user->id}", ['new_owner_id' => '']);
+        $resp->assertRedirect('/settings/users');
+        $this->assertActivityExists(ActivityType::USER_DELETE);
+        $this->assertSessionHas('success');
+    }
+
     public function test_delete_removes_user_preferences()
     {
         $editor = $this->users->editor();